Compare commits

..
Author SHA1 Message Date
irenarindos 37e467c105 chore: update deps 2025-01-07 12:22:59 -05:00
6 changed files with 81 additions and 201 deletions

No files matched your search

+22 -27
View File
@@ -119,17 +119,15 @@ func TestGetCredentialsConfig(t *testing.T) {
Zone: "us-central-1a",
},
expected: &Config{
Zone: "us-central-1a",
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
Zone: "us-central-1a",
Scopes: []string{defaultGCPScope},
},
},
{
name: "nil attributes",
attrs: nil,
expected: &Config{
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
Scopes: []string{defaultGCPScope},
},
},
{
@@ -156,13 +154,12 @@ func TestGetCredentialsConfig(t *testing.T) {
ClientEmail: "test@test.com",
},
expected: &Config{
ProjectId: "test-project",
Zone: "us-central-1a",
ClientEmail: "test@test.com",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: "test-project",
Zone: "us-central-1a",
ClientEmail: "test@test.com",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
Scopes: []string{defaultGCPScope},
},
},
{
@@ -223,13 +220,12 @@ func TestGetCredentialsConfig(t *testing.T) {
Zone: "us-central-1a",
},
expected: &Config{
ProjectId: "test-project",
Zone: "us-central-1a",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test@test.com",
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: "test-project",
Zone: "us-central-1a",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test@test.com",
Scopes: []string{defaultGCPScope},
},
},
{
@@ -245,14 +241,13 @@ func TestGetCredentialsConfig(t *testing.T) {
TargetServiceAccountId: "test-target-service-account",
},
expected: &Config{
ProjectId: "test-project",
Zone: "us-central-1a",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test@test.com",
TargetServiceAccountId: "test-target-service-account",
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: "test-project",
Zone: "us-central-1a",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test@test.com",
TargetServiceAccountId: "test-target-service-account",
Scopes: []string{defaultGCPScope},
},
},
}
+21 -27
View File
@@ -41,21 +41,17 @@ const (
// and then controlling the service account's access by granting it IAM roles.
// https://cloud.google.com/compute/docs/access/service-accounts#scopes_best_practice
defaultGCPScope = "https://www.googleapis.com/auth/cloud-platform"
// defaultValidateServiceAccountKeyTimeout is the timeout for validating
// a service account key.
defaultValidateServiceAccountKeyTimeout = 5 * time.Second
)
// Config is the configuration for the GCP credential.
type Config struct {
ProjectId string
PrivateKey string
PrivateKeyId string
Zone string
ClientEmail string
TargetServiceAccountId string
Scopes []string
validateServiceAccountKeyTimeout time.Duration
ProjectId string
PrivateKey string
PrivateKeyId string
Zone string
ClientEmail string
TargetServiceAccountId string
Scopes []string
}
// credentials represents a simplified version of the GCP credentials file format.
@@ -76,14 +72,13 @@ func NewConfig(opt ...Option) (*Config, error) {
return nil, err
}
c := &Config{
ProjectId: opts.WithProjectId,
PrivateKey: opts.WithPrivateKey,
PrivateKeyId: opts.WithPrivateKeyId,
ClientEmail: opts.WithClientEmail,
TargetServiceAccountId: opts.WithTargetServiceAccountId,
Zone: opts.WithZone,
Scopes: opts.WithScopes,
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: opts.WithProjectId,
PrivateKey: opts.WithPrivateKey,
PrivateKeyId: opts.WithPrivateKeyId,
ClientEmail: opts.WithClientEmail,
TargetServiceAccountId: opts.WithTargetServiceAccountId,
Zone: opts.WithZone,
Scopes: opts.WithScopes,
}
return c, nil
}
@@ -102,14 +97,13 @@ func (c *Config) toCredentials() *credentials {
// clone returns a copy of the configuration.
func (c *Config) clone() *Config {
return &Config{
ProjectId: c.ProjectId,
Zone: c.Zone,
PrivateKey: c.PrivateKey,
PrivateKeyId: c.PrivateKeyId,
ClientEmail: c.ClientEmail,
TargetServiceAccountId: c.TargetServiceAccountId,
Scopes: c.Scopes,
validateServiceAccountKeyTimeout: c.validateServiceAccountKeyTimeout,
ProjectId: c.ProjectId,
Zone: c.Zone,
PrivateKey: c.PrivateKey,
PrivateKeyId: c.PrivateKeyId,
ClientEmail: c.ClientEmail,
TargetServiceAccountId: c.TargetServiceAccountId,
Scopes: c.Scopes,
}
}
+15 -18
View File
@@ -403,14 +403,13 @@ func TestNewConfig(t *testing.T) {
WithScopes([]string{"scope1", "scope2"}),
},
expected: &Config{
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test-email@example.com",
TargetServiceAccountId: "target-service-account@example.com",
Zone: "test-zone",
Scopes: []string{"scope1", "scope2"},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test-email@example.com",
TargetServiceAccountId: "target-service-account@example.com",
Zone: "test-zone",
Scopes: []string{"scope1", "scope2"},
},
expectError: false,
},
@@ -418,8 +417,7 @@ func TestNewConfig(t *testing.T) {
name: "No Options",
options: []Option{},
expected: &Config{
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
Scopes: []string{defaultGCPScope},
},
expectError: false,
},
@@ -444,14 +442,13 @@ func TestNewConfig(t *testing.T) {
WithZone("test-zone"),
},
expected: &Config{
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test-email@example.com",
TargetServiceAccountId: "target-service-account@example.com",
Zone: "test-zone",
Scopes: []string{defaultGCPScope},
validateServiceAccountKeyTimeout: defaultValidateServiceAccountKeyTimeout,
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
PrivateKeyId: "test-private-key-id",
ClientEmail: "test-email@example.com",
TargetServiceAccountId: "target-service-account@example.com",
Zone: "test-zone",
Scopes: []string{defaultGCPScope},
},
expectError: false,
},
+12 -21
View File
@@ -32,6 +32,9 @@ const (
// required to delete a service account key.
// This permission is required to rotate service account keys.
IAMServiceAccountKeysDeletePermission = "iam.serviceAccountKeys.delete"
// validateServiceAccountKeyTimeout is the timeout for validating
// a service account key.
validateServiceAccountKeyTimeout = 5 * time.Second
)
// ServiceAccountPrivateKey represents a decoded PrivateKeyData
@@ -158,31 +161,19 @@ func (c *Config) ValidateServiceAccountKey(
var validatePermissionsErr error
var validationCallbackErr error
ticker := time.NewTicker(200 * time.Millisecond)
defer ticker.Stop()
for start := time.Now(); time.Since(start) < validateServiceAccountKeyTimeout; time.Sleep(200 * time.Millisecond) {
_, validatePermissionsErr = c.ValidateIamPermissions(ctx, permissions, opts...)
ctx, cancel := context.WithTimeout(ctx, c.validateServiceAccountKeyTimeout)
defer cancel()
if validateCredsCallback != nil {
validationCallbackErr = validateCredsCallback(c, opts...)
}
for {
select {
case <-ctx.Done():
if ctx.Err() == context.DeadlineExceeded {
return fmt.Errorf("failed to validate service account key before timeout: %w", errors.Join(validatePermissionsErr, validationCallbackErr))
}
return ctx.Err()
case <-ticker.C:
_, validatePermissionsErr = c.ValidateIamPermissions(ctx, permissions, opts...)
if validateCredsCallback != nil {
validationCallbackErr = validateCredsCallback(c, opts...)
}
if validatePermissionsErr == nil && validationCallbackErr == nil {
return nil
}
if validatePermissionsErr == nil && validationCallbackErr == nil {
return nil
}
}
return errors.Join(validatePermissionsErr, validationCallbackErr)
}
func (c *Config) DeletePrivateKey(ctx context.Context, opts ...option.ClientOption) error {
+6 -108
View File
@@ -7,7 +7,6 @@ import (
"context"
"errors"
"testing"
"time"
"cloud.google.com/go/iam/admin/apiv1/adminpb"
"cloud.google.com/go/iam/apiv1/iampb"
@@ -31,7 +30,6 @@ func TestRotateServiceAccountKey(t *testing.T) {
},
},
}
testValidateTimeout := 1 * time.Second
tests := []struct {
name string
@@ -78,10 +76,9 @@ func TestRotateServiceAccountKey(t *testing.T) {
{
name: "DeleteServiceAccountKey returns error",
config: &Config{
PrivateKey: "some-private-key",
PrivateKeyId: "some-private-key-id",
ClientEmail: "client@example.com",
validateServiceAccountKeyTimeout: testValidateTimeout,
PrivateKey: "some-private-key",
PrivateKeyId: "some-private-key-id",
ClientEmail: "client@example.com",
},
setup: func() {
testIAMAdminServer.testCreateServiceAccountKeyError = nil
@@ -92,10 +89,9 @@ func TestRotateServiceAccountKey(t *testing.T) {
{
name: "Successful rotation",
config: &Config{
PrivateKey: "some-private-key",
PrivateKeyId: "some-private-key-id",
ClientEmail: "client@example.com",
validateServiceAccountKeyTimeout: testValidateTimeout,
PrivateKey: "some-private-key",
PrivateKeyId: "some-private-key-id",
ClientEmail: "client@example.com",
},
setup: func() {
testIAMAdminServer.testCreateServiceAccountKeyError = nil
@@ -265,104 +261,6 @@ func TestValidateIamPermissions(t *testing.T) {
})
}
}
func TestValidateServiceAccountKey(t *testing.T) {
ctx := context.Background()
testResourceServer := &testResourceServer{}
testValidateTimeout := 1 * time.Second
tests := []struct {
name string
config *Config
permissions []string
validateCredsCallback ValidateCredsCallback
testIamPermissionsError error
expectedError error
}{
{
name: "Successful validation",
config: &Config{
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
ClientEmail: "test@test.com",
validateServiceAccountKeyTimeout: testValidateTimeout,
},
permissions: []string{
ComputeInstancesListPermission,
IAMServiceAccountKeysCreatePermission,
IAMServiceAccountKeysDeletePermission,
},
validateCredsCallback: func(c *Config, opts ...googleOption.ClientOption) error {
return nil
},
expectedError: nil,
},
{
name: "Validation callback returns error",
config: &Config{
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
ClientEmail: "test@test.com",
validateServiceAccountKeyTimeout: testValidateTimeout,
},
permissions: []string{
ComputeInstancesListPermission,
IAMServiceAccountKeysCreatePermission,
IAMServiceAccountKeysDeletePermission,
},
validateCredsCallback: func(c *Config, opts ...googleOption.ClientOption) error {
return errors.New("validation callback error")
},
expectedError: errors.New("validation callback error"),
},
{
name: "IAM permissions validation returns error",
config: &Config{
ProjectId: "test-project-id",
PrivateKey: "test-private-key",
ClientEmail: "test@test.com",
validateServiceAccountKeyTimeout: testValidateTimeout,
},
permissions: []string{
ComputeInstancesListPermission,
IAMServiceAccountKeysCreatePermission,
IAMServiceAccountKeysDeletePermission,
},
testIamPermissionsError: errors.New("IAM permissions error"),
expectedError: errors.New("failed to validate IAM permissions"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testResourceServer.testIamPermissionsResponse = &iampb.TestIamPermissionsResponse{
Permissions: tt.permissions,
}
testResourceServer.testIamPermissionsError = tt.testIamPermissionsError
gsrv := NewGRPCServer()
resourcemanagerpb.RegisterProjectsServer(gsrv.Server, testResourceServer)
addr, err := gsrv.Start()
require.NoError(t, err)
testOptions := []googleOption.ClientOption{
googleOption.WithEndpoint(addr),
googleOption.WithoutAuthentication(),
googleOption.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
googleOption.WithTokenSource(nil),
}
err = tt.config.ValidateServiceAccountKey(ctx, tt.permissions, tt.validateCredsCallback, testOptions...)
if tt.expectedError != nil {
require.ErrorContains(t, err, tt.expectedError.Error())
return
}
require.NoError(t, err)
})
}
}
func TestDeletePrivateKey(t *testing.T) {
testIAMAdminServer := &testIAMAdminServer{}
+5
View File
@@ -617,6 +617,11 @@ func validateSet(s *hostsets.HostSet) error {
}
badFields := make(map[string]string)
_, filterSet := attrMap[ConstListInstancesFilter]
if filterSet && len(attrs.Filters) == 0 {
badFields[fmt.Sprintf("attributes.%s", ConstListInstancesFilter)] = "must not be empty"
}
for f := range attrMap {
if _, ok := allowedSetFields[f]; !ok {