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

all: define a meta-interface for IAM #340

Closed
broady opened this issue Aug 31, 2016 · 24 comments
Closed

all: define a meta-interface for IAM #340

broady opened this issue Aug 31, 2016 · 24 comments
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Milestone

Comments

@broady
Copy link
Contributor

broady commented Aug 31, 2016

Many APIs (pubsub and storage are probably the most prominent) need to modify IAM roles.

Someone needs to define a meta-interface so that the IAM functionality is consistent across packages.

Maybe something similar to storage.ACLHandle?

@broady
Copy link
Contributor Author

broady commented Aug 31, 2016

cc @rakyll @jba @light

@jba
Copy link
Contributor

jba commented Sep 1, 2016

The way the IAM methods are factored into a separate service that takes an arbitrary resource name makes it easy for service implementors, but not so much for users. We probably don't want to expose that directly, because specifying a resource name correctly can be a challenge.

I think that for clients that represent each entity as a separate type, like pubsub, each such type (e.g. pubsub.Topic) should have its own set of methods:

  • GetIAMPolicy(ctx) (*Policy, error)
  • SetIAMPolicy(ctx, *Policy) (*Policy, error)
  • TestIAMPermissions(ctx, perms []string) ([]string, error)

The resource name will be handled entirely by the client.

Other clients may add a string field to those methods denoting a suffix of the full resource name. For example, the new logging client uses logIDs (not full log resource names), so it could have IAM methods that take log IDs.

We may want to wrap the Policy proto and put it in a package like cloud.google.com/go/iam, or we may just want to leave it as a proto. It translates into a pretty straightforward Go type.

@okdave
Copy link
Contributor

okdave commented Sep 2, 2016

Instead of adding those methods to each type, could we have a central IAM package which can deal with arbitrary types? That would keep the signatures for these basic types much less polluted.

I imagine we could do something like:

package iam

func (c *Client?) GetPolicy(x interface{}) (*Policy, error) {
  id := internal.Lookup(x)  // Say.
  if id == "" { return nil, fmt.Errorf("unknown type %T", x) }
}
package pubsub

func init() {
  internal.RegisterType(func(t *Topic) string {return t.id() })
  etc.
}

That's a very, very rough sketch – would something like that work?

@okdave
Copy link
Contributor

okdave commented Sep 2, 2016

One interesting thing which might influence the design: IIUC the IAM service has it's own endpoint with specific scopes (https://iam.googleapis.com/v1/* and iam.*) – that might indicate we need a separate Client type anyway?

@shantuo
Copy link
Contributor

shantuo commented Sep 6, 2016

Yes, I realized that we probably need another Client for iam when I looked into the code.

While from a user's point of view, I'd love to see a simple and straightforward interface like what @jba mentioned. Maybe we can have an iam package for the client and put the iam.Client into each major API (storage and pubsub)? In that way user can use all the iam methods without directly use the iam package.

@broady
Copy link
Contributor Author

broady commented Sep 7, 2016

The iam package is actually a little different - it's for managing service accounts and keys only.

Each API has its own methods for IAM operations.

I'm a bit biased, but I liked what I did with the storage package.

Something like

pubsubTopic.IAM() // type cloud.IAMPolicy (or something else)

policy, err := pubsubTopic.IAM().Get()

policy, err := pubsubTopic.IAM().SetRole(...) // Handles Get/Modify atomically
policy, err := pubsubTopic.IAM().RemoveRole(...) // Handles Get/Modify atomically

package cloud

type IAMPolicy interface {
  Get()
  Set(...)
  SetRole(...)
  RemoveRole(...)
}

This way, we don't have three+ (more in future) methods for IAM for each resource in our packages.

@jba
Copy link
Contributor

jba commented Sep 7, 2016

I like @broady's idea because it's clear which things have IAM policies.

We could also consider a variant of @okdave's idea:

type IAMer interface { iamResourceName() string }

func (c *Client) GetIAMPolicy(x IAMer) ...

We'd repeat this in each client.

There is no additional user-visible surface for each type that supports IAM, but the IAM calls are checked at compile-time. The downside is that you'd have to read the docs to know which types are IAMers. We already have precedent for this: the bigquery Copy method, and Source and Destination interfaces.

@jba jba added the type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. label Sep 11, 2016
@jba jba added this to the 2016 Q4 milestone Sep 11, 2016
@elibixby
Copy link

FYI you're also going to have to implement testIAMPermissions and queryGrantableRoles as they are part of the IAM meta interface. Although they aren't super important right now they will be come very important once Custom Roles drop Soon (TM).

@jba
Copy link
Contributor

jba commented Oct 5, 2016

This is now a priority for early Q4 so let's find a design we can agree on. I know @pongad is also thinking about this.

I think @broady has the best idea so far: each IAM-enabled resource has one additional method, IAM, which returns a value that handles all IAM functionality for that resource.

Pros:

  • Clear from the doc what resources support IAM.
  • Only one method per resource.
  • IAM support can live in a single package common to all clients. Not only do we factor out the implementation, we also factor out the doc--the only added doc weight is the per-resource IAM method.

@jba
Copy link
Contributor

jba commented Oct 5, 2016

@jba
Copy link
Contributor

jba commented Oct 6, 2016

One complication: it looks like QueryGrantableRoles is defined on the IAM API itself, not the per-resource meta-API. That means we'll need a separate client for that RPC.

@jba
Copy link
Contributor

jba commented Oct 10, 2016

Here is where the design now stands. Unfortunately, it has a significant problem.

The design follows the split built in to IAM, which separates common per-resource operations that are implemented by each Google API (Get/Set Policy and TestPermissions) from administrative actions supported by a separate IAM service.

  • There is an iam package (CL) that defines a Handle. Each API client will provide one method named IAM on each IAM-enable resource that returns a Handle. Handles support GetPolicy, SetPolicy and TestPermissions. For example, pubsub topics support IAM, so pubsub.Topic has an IAM method that returns an *iam.Handle.
  • There is a separate package for the IAM admin API. That package will be generated (CL).

The problem is that both IAM packages refer to Policy, but in the handwritten package this is the handwritten iam.Policy type, while in the generated code it is the Policy proto message. The Policy message appears in the admin API because one job of the admin API is to manage policies for service accounts. In other words, service accounts are an IAM-enabled resource like pubsub topics, and the service that manages them is the IAM admin service.

As I see it, we have these options:

  1. Do nothing. The few (?) users who deal with service account policies will have to work with the generated code and the raw proto. The handwritten Policy type has a few convenience methods for adding and removing members and the like, so working with the raw proto is mildly unpleasant by comparison. But if it's rare, then that is not a big deal.
  2. Have the generated code refer to the handwritten Policy type instead of the proto. This is a nice solution if it's feasible. @pongad, is it?
  3. Export conversions between the two Policy types from the iam package. This clutters the package with raw protos, but is easy to do and will allow users of service account policies to have a slightly more pleasant time.
  4. Write a handwritten IAM admin client. This is the most work and the gain is unclear. @jgeewax, @omaray, do we want handwritten IAM admin clients?

@s-mang
Copy link

s-mang commented Oct 10, 2016

I vote for #1. If we do not have a hand-written client for service-account-IAM b/c of lack of a use case, I don't see a need to support service-account-IAM in any other hand-written clients.
If we later decide there is a need for a hand-written client for the service-account-IAM proto, then I think we should incorporate it into our other hand-written clients.

We should make the decision to incorporate or not incorporate service accounts in hand-written IAM packages across the board.

@jba
Copy link
Contributor

jba commented Oct 10, 2016

I'm OK with (1), but one correction: we would only implement one handwritten client for service accounts: the IAM admin client. All service accounts are managed from that one API.

@s-mang
Copy link

s-mang commented Oct 10, 2016

Oh ok, I misunderstood then.
So we are writing:

  1. a handwritten client for service account IAM with all of the extensive functionality that its proto supports
  2. another handwritten IAM package for non-service accounts with the three bits of functionality supported in the proto for general IAM
  3. IAM accessor methods on top of the existing handwritten clients for each resource for each GCP service

and the question is whether or not we need to have support for service account IAM in (3).

Is that correct?

@jba
Copy link
Contributor

jba commented Oct 10, 2016

Not quite. We are doing (2) and (3), but your (1) is my original (4) -- in other words, it's a possibility, but not definite (and is actually my least favorite choice).

@s-mang
Copy link

s-mang commented Oct 10, 2016

Ok. So when you say "we would only implement one handwritten client for service accounts" in your last comment, what does that mean if not my (1)?

@jba
Copy link
Contributor

jba commented Oct 10, 2016

I was responding to your " I don't see a need to support service-account-IAM in any other hand-written clients." I was pointing out that if we supported service-account IAM, we would do so by writing only one handwritten client, the one for IAM admin. In other words, I was correcting your impression of multiple service-account clients, when there would be only one. I wasn't saying we would actually write it.

@s-mang
Copy link

s-mang commented Oct 10, 2016

Oh! Sorry for the confusion.

Ok, I'm still for (1) then until we get user requests for something better.

@pongad
Copy link
Contributor

pongad commented Oct 11, 2016

@adams-sarah @jba 2. is a little tricky. Our generator is built around protobuf messages, and so making it refer to hand-written code in this way might require some dark magic.

Another solution is to tell the generator to not generate code for the 3 methods that refers to Policy. Then, in a separate file, hand-write just those 3 methods. (Go allows methods of one type to span multiple files right?) If we want to do this, it would be the first time we generate/write this kind of "cyborg" code. There's a bit of unknown here, but it might be worth exploring.

cc @garrettjonesgoogle

@jba
Copy link
Contributor

jba commented Oct 11, 2016

The cyborg idea is something we've discussed before, in the abstract. It could definitely work here. @pongad, can you generate such a client and mail me the CL? I'll take it from there.

@pongad
Copy link
Contributor

pongad commented Oct 12, 2016

@jba googleapis/gapic-generator#599 should provide the support needed to do this kind of stuff. I'll work on this after the PR lands. If you want it sooner, please let me know.

pongad added a commit to googleapis/googleapis that referenced this issue Oct 14, 2016
This commit disable iam admin's GetIamPolicy, SetIamPolicy,
and TestIamPermissions.

These methods are to be handwritten later.

Update googleapis/google-cloud-go#340
okdave pushed a commit that referenced this issue Oct 14, 2016
Introduces iam.Handle, which will be returned by the IAM method of
all resources that support IAM.

Currently we support only the standard IAM methods Get, Set and
TestPermissions. We can consider adding convenience methods like
AddMemberToRole, but that is quite easy to write:

    h := pubsubTopic.IAM()
    policy, err := h.Policy(ctx)
    if err != nil { ... }
    policy.Add(iam.AllUsers, iam.Viewer)
    if err := ph.SetPolicy(ctx, policy); err != nil { ... }

That code includes an ETag check.

We don't attempt to support QueryGrantableRoles here. Although it's
per-resource, it's actually part of the admin API, which has its own
service. It's ultimately cleaner to keep them separate.

Issue #340

Change-Id: I688a39c69c0ccffc46f0d506b2105f2bdbf4dfe3
Reviewed-on: https://code-review.googlesource.com/8130
Reviewed-by: Chris Broadfoot <cbro@google.com>
Reviewed-by: Ross Light <light@google.com>
bjwatson pushed a commit to googleapis/googleapis that referenced this issue Oct 17, 2016
This commit disable iam admin's GetIamPolicy, SetIamPolicy,
and TestIamPermissions.

These methods are to be handwritten later.

Update googleapis/google-cloud-go#340
@pongad
Copy link
Contributor

pongad commented Oct 18, 2016

Update: https://code-review.googlesource.com/#/c/8491/ has landed. The IAM admin should be ready to get cyborgified now.

@jba
Copy link
Contributor

jba commented Oct 31, 2016

This particular issue was addressed in fc7a628.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

7 participants