Skip to content

Commit

Permalink
Replace role-based alias config with mount based alias config (#91) (#96
Browse files Browse the repository at this point in the history
)

This moves the role-based alias configuration fields (`iam_alias`,
`gce_alias` from an individual role to the GCP configuration. This will
allow the user to specify the configuration for all roles rather than
per-role.
  • Loading branch information
pcman312 committed Apr 28, 2020
1 parent 43bcca9 commit b973f97
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 408 deletions.
17 changes: 0 additions & 17 deletions plugin/aliasing.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"sort"
"strconv"
"strings"

"google.golang.org/api/compute/v1"
"google.golang.org/api/iam/v1"
Expand Down Expand Up @@ -73,19 +72,3 @@ func getGCEInstanceID(_ *gcpRole, instance *compute.Instance) (alias string) {
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
}
149 changes: 0 additions & 149 deletions plugin/aliasing_test.go
Original file line number Diff line number Diff line change
@@ -1,150 +1 @@
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)
}
})
}
}
113 changes: 113 additions & 0 deletions plugin/gcp_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package gcpauth

import (
"encoding/json"
"errors"
"fmt"
"strings"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-gcp-common/gcputil"
"github.com/hashicorp/vault/sdk/framework"
"google.golang.org/api/compute/v1"
"google.golang.org/api/iam/v1"
)

// gcpConfig contains all config required for the GCP backend.
type gcpConfig struct {
Credentials *gcputil.GcpCredentials `json:"credentials"`
IAMAliasType string `json:"iam_alias"`
GCEAliasType string `json:"gce_alias"`
}

// standardizedCreds wraps gcputil.GcpCredentials with a type to allow
// parsing through Google libraries, since the google libraries struct is not
// exposed.
type standardizedCreds struct {
*gcputil.GcpCredentials
CredType string `json:"type"`
}

const serviceAccountCredsType = "service_account"

// formatAsCredentialJSON converts and marshals the config credentials
// into a parsable format by Google libraries.
func (c *gcpConfig) formatAndMarshalCredentials() ([]byte, error) {
if c == nil || c.Credentials == nil {
return []byte{}, nil
}

return json.Marshal(standardizedCreds{
GcpCredentials: c.Credentials,
CredType: serviceAccountCredsType,
})
}

// Update sets gcpConfig values parsed from the FieldData.
func (c *gcpConfig) Update(d *framework.FieldData) (bool, error) {
if d == nil {
return false, nil
}

changed := false

if v, ok := d.GetOk("credentials"); ok {
creds, err := gcputil.Credentials(v.(string))
if err != nil {
return false, errwrap.Wrapf("failed to read credentials: {{err}}", err)
}

if len(creds.PrivateKeyId) == 0 {
return false, errors.New("missing private key in credentials")
}

c.Credentials = creds
changed = true
}

rawIamAlias, exists := d.GetOk("iam_alias")
if exists {
iamAlias := rawIamAlias.(string)
if iamAlias != c.IAMAliasType {
c.IAMAliasType = iamAlias
changed = true
}
}

rawGceAlias, exists := d.GetOk("gce_alias")
if exists {
gceAlias := rawGceAlias.(string)
if gceAlias != c.GCEAliasType {
c.GCEAliasType = gceAlias
changed = true
}
}

return changed, nil
}

func (c *gcpConfig) getIAMAlias(role *gcpRole, svcAccount *iam.ServiceAccount) (alias string, err error) {
aliasType := c.IAMAliasType
if aliasType == "" {
aliasType = defaultIAMAlias
}

aliaser, exists := allowedIAMAliases[aliasType]
if !exists {
return "", fmt.Errorf("invalid IAM alias type: must be one of: %s", strings.Join(allowedIAMAliasesSlice, ", "))
}
return aliaser(role, svcAccount), nil
}

func (c *gcpConfig) getGCEAlias(role *gcpRole, instance *compute.Instance) (alias string, err error) {
aliasType := c.GCEAliasType
if aliasType == "" {
aliasType = defaultGCEAlias
}

aliaser, exists := allowedGCEAliases[aliasType]
if !exists {
return "", fmt.Errorf("invalid GCE alias type: must be one of: %s", strings.Join(allowedGCEAliasesSlice, ", "))
}
return aliaser(role, instance), nil
}

0 comments on commit b973f97

Please sign in to comment.