-
Notifications
You must be signed in to change notification settings - Fork 25
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
Add alias types for IAM and GCE logins #89
Merged
+1,635
−422
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
1391e80
Add alias types for IAM and GCE logins
pcman312 6bb1c8f
Remove commented out code
pcman312 d0ab734
Removed unused variable
pcman312 275be17
Minor cleanup
pcman312 c3ba638
retrieveRole -> role for consistency across plugins
pcman312 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package gcpauth | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
|
||
"google.golang.org/api/compute/v1" | ||
"google.golang.org/api/iam/v1" | ||
) | ||
|
||
type iamAliaser func(role *gcpRole, svcAccount *iam.ServiceAccount) (alias string) | ||
type gceAliaser func(role *gcpRole, instance *compute.Instance) (alias string) | ||
|
||
const ( | ||
defaultIAMAlias = "unique_id" | ||
defaultGCEAlias = "instance_id" | ||
) | ||
|
||
var ( | ||
allowedIAMAliases = map[string]iamAliaser{ | ||
defaultIAMAlias: getIAMSvcAccountUniqueID, | ||
"": getIAMSvcAccountUniqueID, // For backwards compatibility | ||
|
||
"role_id": getIAMRoleID, | ||
} | ||
allowedGCEAliases = map[string]gceAliaser{ | ||
defaultGCEAlias: getGCEInstanceID, | ||
"": getGCEInstanceID, // For backwards compatibility | ||
|
||
"role_id": getGCERoleID, | ||
} | ||
|
||
allowedIAMAliasesSlice = iamMapKeyToSlice(allowedIAMAliases) | ||
allowedGCEAliasesSlice = gceMapKeyToSlice(allowedGCEAliases) | ||
) | ||
|
||
func iamMapKeyToSlice(m map[string]iamAliaser) (s []string) { | ||
for key := range m { | ||
if key == "" { | ||
continue | ||
} | ||
s = append(s, key) | ||
} | ||
sort.Strings(s) | ||
return s | ||
} | ||
|
||
func gceMapKeyToSlice(m map[string]gceAliaser) (s []string) { | ||
for key := range m { | ||
if key == "" { | ||
continue | ||
} | ||
s = append(s, key) | ||
} | ||
sort.Strings(s) | ||
return s | ||
} | ||
|
||
func getIAMSvcAccountUniqueID(_ *gcpRole, svcAccount *iam.ServiceAccount) (alias string) { | ||
return svcAccount.UniqueId | ||
} | ||
|
||
func getIAMRoleID(role *gcpRole, _ *iam.ServiceAccount) (alias string) { | ||
return role.RoleID | ||
} | ||
|
||
func getGCEInstanceID(_ *gcpRole, instance *compute.Instance) (alias string) { | ||
return fmt.Sprintf("gce-%s", strconv.FormatUint(instance.Id, 10)) | ||
} | ||
|
||
func getGCERoleID(role *gcpRole, _ *compute.Instance) (alias string) { | ||
return role.RoleID | ||
} | ||
|
||
func getIAMAlias(role *gcpRole, svcAccount *iam.ServiceAccount) (alias string, err error) { | ||
aliaser, exists := allowedIAMAliases[role.IAMAliasType] | ||
if !exists { | ||
return "", fmt.Errorf("invalid IAM alias type: must be one of: %s", strings.Join(allowedIAMAliasesSlice, ", ")) | ||
} | ||
return aliaser(role, svcAccount), nil | ||
} | ||
|
||
func getGCEAlias(role *gcpRole, instance *compute.Instance) (alias string, err error) { | ||
aliaser, exists := allowedGCEAliases[role.GCEAliasType] | ||
if !exists { | ||
return "", fmt.Errorf("invalid GCE alias type: must be one of: %s", strings.Join(allowedIAMAliasesSlice, ", ")) | ||
} | ||
return aliaser(role, instance), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package gcpauth | ||
|
||
import ( | ||
"testing" | ||
|
||
"google.golang.org/api/compute/v1" | ||
"google.golang.org/api/iam/v1" | ||
) | ||
|
||
func TestGetIAMAlias(t *testing.T) { | ||
type testCase struct { | ||
role *gcpRole | ||
svcAccount *iam.ServiceAccount | ||
expectedAlias string | ||
expectErr bool | ||
} | ||
|
||
tests := map[string]testCase{ | ||
"invalid type": { | ||
role: &gcpRole{ | ||
IAMAliasType: "bogus", | ||
RoleID: "testRoleID", | ||
}, | ||
svcAccount: &iam.ServiceAccount{ | ||
UniqueId: "iamUniqueID", | ||
}, | ||
expectedAlias: "", | ||
expectErr: true, | ||
}, | ||
"empty type goes to default": { | ||
role: &gcpRole{ | ||
IAMAliasType: "", | ||
RoleID: "testRoleID", | ||
}, | ||
svcAccount: &iam.ServiceAccount{ | ||
UniqueId: "iamUniqueID", | ||
}, | ||
expectedAlias: "iamUniqueID", | ||
expectErr: false, | ||
}, | ||
"default type": { | ||
role: &gcpRole{ | ||
IAMAliasType: defaultIAMAlias, | ||
RoleID: "testRoleID", | ||
}, | ||
svcAccount: &iam.ServiceAccount{ | ||
UniqueId: "iamUniqueID", | ||
}, | ||
expectedAlias: "iamUniqueID", | ||
expectErr: false, | ||
}, | ||
"role_id": { | ||
role: &gcpRole{ | ||
IAMAliasType: "role_id", | ||
RoleID: "testRoleID", | ||
}, | ||
svcAccount: &iam.ServiceAccount{ | ||
UniqueId: "iamUniqueID", | ||
}, | ||
expectedAlias: "testRoleID", | ||
expectErr: false, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
actualAlias, err := getIAMAlias(test.role, test.svcAccount) | ||
if test.expectErr && err == nil { | ||
t.Fatalf("err expected, got nil") | ||
} | ||
if !test.expectErr && err != nil { | ||
t.Fatalf("no error expected, got: %s", err) | ||
} | ||
if actualAlias != test.expectedAlias { | ||
t.Fatalf("Actual alias: %s Expected Alias: %s", actualAlias, test.expectedAlias) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGetGCEAlias(t *testing.T) { | ||
type testCase struct { | ||
role *gcpRole | ||
instance *compute.Instance | ||
expectedAlias string | ||
expectErr bool | ||
} | ||
|
||
tests := map[string]testCase{ | ||
"invalid type": { | ||
role: &gcpRole{ | ||
GCEAliasType: "bogus", | ||
RoleID: "testRoleID", | ||
}, | ||
instance: &compute.Instance{ | ||
Id: 123, | ||
}, | ||
expectedAlias: "", | ||
expectErr: true, | ||
}, | ||
"empty type goes to default": { | ||
role: &gcpRole{ | ||
GCEAliasType: "", | ||
RoleID: "testRoleID", | ||
}, | ||
instance: &compute.Instance{ | ||
Id: 123, | ||
}, | ||
expectedAlias: "gce-123", | ||
expectErr: false, | ||
}, | ||
"default type": { | ||
role: &gcpRole{ | ||
GCEAliasType: defaultGCEAlias, | ||
RoleID: "testRoleID", | ||
}, | ||
instance: &compute.Instance{ | ||
Id: 123, | ||
}, | ||
expectedAlias: "gce-123", | ||
expectErr: false, | ||
}, | ||
"role_id": { | ||
role: &gcpRole{ | ||
GCEAliasType: "role_id", | ||
RoleID: "testRoleID", | ||
}, | ||
instance: &compute.Instance{ | ||
Id: 123, | ||
}, | ||
expectedAlias: "testRoleID", | ||
expectErr: false, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
actualAlias, err := getGCEAlias(test.role, test.instance) | ||
if test.expectErr && err == nil { | ||
t.Fatalf("err expected, got nil") | ||
} | ||
if !test.expectErr && err != nil { | ||
t.Fatalf("no error expected, got: %s", err) | ||
} | ||
if actualAlias != test.expectedAlias { | ||
t.Fatalf("Actual alias: %s Expected Alias: %s", actualAlias, test.expectedAlias) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
These could probably be reduced into a single function that accepts an interface and is type switched. Same with many of the
get..
functions below.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.
Unfortunately it wouldn't really help here. The function would have the same code in it (including both versions of the for loop), but wrapped up into a single function rather than two. There isn't a generic
map
type that can be used as a variable type that would allow the type coercion needed here. Additionally it's generally frowned upon to pass the empty interface around unless necessary.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 have generally found the same thing as well - when I have tried that approach it read awkwardly, but just in a different way. I don't think there's any "perfect" answer here, but I do like the more direct approach taken here because type conversions can be expensive.
Thank you for your review, by the way, broamski!