-
Notifications
You must be signed in to change notification settings - Fork 24
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
Handle version 3 policies for Resource Manager IAM requests ([projects, folders, organziations].getIamPolicy) #77
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,3 +124,171 @@ func TestParsedIamResource(t *testing.T) { | |
} | ||
} | ||
} | ||
|
||
func TestConditionalIamResource(t *testing.T) { | ||
r := &parsedIamResource{ | ||
relativeId: &gcputil.RelativeResourceName{ | ||
Name: "projects", | ||
TypeKey: "cloudresourcemanager/projects", | ||
IdTuples: map[string]string{ | ||
"projects": "project", | ||
}, | ||
OrderedCollectionIds: []string{"cloudresourcemanager", "projects"}, | ||
}, | ||
config: &IamRestResource{ | ||
Name: "projects", | ||
TypeKey: "cloudresourcemanager/projects", | ||
Service: "cloudresourcemanager", | ||
IsPreferredVersion: true, | ||
GetMethod: RestMethod{ | ||
HttpMethod: "GET", | ||
BaseURL: "https://cloudresourcemanager.googleapis.com/v1/", | ||
Path: "projects/{resource}:getIamPolicy", | ||
}, | ||
SetMethod: RestMethod{ | ||
HttpMethod: "POST", | ||
BaseURL: "https://cloudresourcemanager.googleapis.com/v1/", | ||
Path: "projects/{resource}:setIamPolicy", | ||
RequestFormat: `{"policy":%s}`, | ||
}, | ||
Parameters: []string{"resource"}, | ||
CollectionReplacementKeys: map[string]string{ | ||
"projects": "resource"}, | ||
}, | ||
} | ||
|
||
getR, err := r.GetIamPolicyRequest() | ||
if err != nil { | ||
t.Fatalf("Could not construct GetIamPolicyRequest: %v", err) | ||
} | ||
expectedURLBase := "https://cloudresourcemanager.googleapis.com/v1/projects/project" | ||
if getR.URL.String() != expectedURLBase+":getIamPolicy" { | ||
t.Fatalf("expected get request URL %s, got %s", expectedURLBase+":getIamPolicy", getR.URL.String()) | ||
} | ||
if getR.Method != "GET" { | ||
t.Fatalf("expected get request method %s, got %s", "GET", getR.Method) | ||
} | ||
if getR.Body == nil { | ||
t.Fatalf("expected non-nil get body") | ||
} | ||
data, err := ioutil.ReadAll(getR.Body) | ||
if err != nil { | ||
t.Fatalf("Error reading data from request body %v", err) | ||
} | ||
var body interface{} | ||
err = json.Unmarshal(data, &body) | ||
if err != nil { | ||
t.Fatalf("Error parsing json from request body %s %v", string(data), err) | ||
} | ||
reqBody, ok := body.(map[string]interface{}) | ||
if !ok { | ||
t.Fatalf("Error asserting request body %s", string(data)) | ||
} | ||
options, ok := reqBody["options"] | ||
if !ok { | ||
t.Fatalf("Couldn't find options in request body %s", string(data)) | ||
} | ||
|
||
optionsMap, ok := options.(map[string]interface{}) | ||
if !ok { | ||
t.Fatalf("Error asserting options in request body %s", string(data)) | ||
} | ||
|
||
requestedPolicyVersion, ok := optionsMap["requestedPolicyVersion"] | ||
if !ok { | ||
t.Fatalf("Couldn't find requestedPolicytVersion in options in request body %s", string(data)) | ||
} | ||
|
||
version, ok := requestedPolicyVersion.(float64) | ||
if !ok { | ||
t.Fatalf("Error asserting requestedPolicyVersion in request body %s", string(data)) | ||
} | ||
|
||
if version != 3 { | ||
t.Fatalf("requestedPolicyVersion is not 3 in request body %s", string(data)) | ||
} | ||
|
||
expectedP := &Policy{ | ||
Etag: "atag", | ||
Version: 3, | ||
Bindings: []*Binding{ | ||
{ | ||
Members: []string{"user:[email protected]", "serviceAccount:[email protected]"}, | ||
Role: "roles/arole", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it allows you to set this role against the API? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I copied this from the unit test above https://github.com/hashicorp/vault-plugin-secrets-gcp/pull/77/files/d3f88e4a116df44984e0b53863d830ddcebfbe30#diff-7a6661e66767e3ea368ba89fe6d9ca97R68 |
||
Condition: &Condition{ | ||
Title: "test", | ||
Description: "", | ||
Expression: "a==b", | ||
}, | ||
}, | ||
{ | ||
Members: []string{"user:[email protected]"}, | ||
Role: "roles/anotherrole", | ||
}, | ||
}, | ||
} | ||
setR, err := r.SetIamPolicyRequest(expectedP) | ||
if err != nil { | ||
t.Fatalf("Could not construct SetIamPolicyRequest: %v", err) | ||
} | ||
|
||
if setR.URL.String() != expectedURLBase+":setIamPolicy" { | ||
t.Fatalf("expected set request URL %s, got %s", expectedURLBase+":setIamPolicy", getR.URL.String()) | ||
} | ||
if setR.Method != "POST" { | ||
t.Fatalf("expected set request method %s, got %s", "POST", getR.Method) | ||
} | ||
if setR.Header.Get("Content-Type") != "application/json" { | ||
t.Fatalf("expected `Content Type = application/json` header in set request, headers: %+v", setR.Header) | ||
} | ||
if setR.Body == nil { | ||
t.Fatalf("expected non-nil set body, actually nil") | ||
} | ||
data, err = ioutil.ReadAll(setR.Body) | ||
if err != nil { | ||
t.Fatalf("unable to read data from set request: %v", err) | ||
} | ||
|
||
actual := struct { | ||
P *Policy `json:"policy,omitempty"` | ||
}{} | ||
if err := json.Unmarshal(data, &actual); err != nil { | ||
t.Fatalf("unable to read policy from set request body: %v", err) | ||
} | ||
if actual.P.Etag != expectedP.Etag { | ||
t.Fatalf("mismatch set request policy, expected %s, got %s", expectedP.Etag, actual.P.Etag) | ||
} | ||
|
||
if len(actual.P.Bindings) != len(expectedP.Bindings) { | ||
t.Fatalf("mismatch set request policy bindings length, expected %+v, got %+v", expectedP.Bindings, actual.P.Bindings) | ||
} | ||
|
||
for i, expectB := range expectedP.Bindings { | ||
actualB := actual.P.Bindings[i] | ||
if expectB.Role != actualB.Role { | ||
t.Errorf("expected bindings[%d] to have role %s, got %s", i, expectB.Role, actualB.Role) | ||
} | ||
if len(expectB.Members) != len(actualB.Members) { | ||
t.Errorf("expected bindings[%d] to have members %+v, got %+v", i, expectB.Members, actualB.Members) | ||
} | ||
for memberI, expectM := range expectB.Members { | ||
if expectM != actualB.Members[memberI] { | ||
t.Errorf("expected bindings[%d], members[%d] to be %s, got %s", i, memberI, expectM, actualB.Members[memberI]) | ||
} | ||
} | ||
if expectB.Condition != nil { | ||
if actualB.Condition == nil { | ||
t.Errorf("expected bindings[%d] to have condition %s, got %s", i, expectB.Condition, actualB.Condition) | ||
} | ||
if expectB.Condition.Title != actualB.Condition.Title { | ||
t.Errorf("expected bindings[%d] to have condition titled %s, got %s", i, expectB.Condition.Title, actualB.Condition.Title) | ||
} | ||
if expectB.Condition.Description != actualB.Condition.Description { | ||
t.Errorf("expected bindings[%d] to have condition description %s, got %s", i, expectB.Condition.Description, actualB.Condition.Description) | ||
} | ||
if expectB.Condition.Expression != actualB.Condition.Expression { | ||
t.Errorf("expected bindings[%d] to have condition expression %s, got %s", i, expectB.Condition.Expression, actualB.Condition.Expression) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a huge fan of this check against a specific service, but I'm ok with adding this for now to unblock - will need to build some things in for conditions, as it varies by service whether the value is included or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand, it's definitely a one-off check as there are other services that aren't included that also support IAM conditions.
I wasn't sure how to scrape that information from the docs into the
generatedIamResources
, perhaps it could be hardcoded in the generation tool as a service map likecorrectedRequestFormats
. And thenLatestPolicyVersion
could be added as an optional field inIamRestResource
which could be used to replace this specific service conditional.