diff --git a/plugins/providers/bigquery/client.go b/plugins/providers/bigquery/client.go index 94ac03c70..75633642c 100644 --- a/plugins/providers/bigquery/client.go +++ b/plugins/providers/bigquery/client.go @@ -98,7 +98,7 @@ func (c *bigQueryClient) GetTables(ctx context.Context, datasetID string) ([]*Ta return results, nil } -func (c *bigQueryClient) GrantDatasetAccess(ctx context.Context, d *Dataset, user, role string) error { +func (c *bigQueryClient) GrantDatasetAccess(ctx context.Context, d *Dataset, accountType, accountID, role string) error { dataset := c.client.Dataset(d.DatasetID) metadata, err := dataset.Metadata(ctx) if err != nil { @@ -106,15 +106,19 @@ func (c *bigQueryClient) GrantDatasetAccess(ctx context.Context, d *Dataset, use } for _, a := range metadata.Access { - if a.Entity == user && string(a.Role) == role { + if a.Entity == accountID && string(a.Role) == role { return ErrPermissionAlreadyExists } } + entityType := bq.UserEmailEntity + if accountType == AccountTypeGroup { + entityType = bq.GroupEmailEntity + } update := bq.DatasetMetadataToUpdate{ Access: append(metadata.Access, &bq.AccessEntry{ Role: bq.AccessRole(role), - EntityType: bq.UserEmailEntity, - Entity: user, + EntityType: entityType, + Entity: accountID, }), } @@ -122,16 +126,20 @@ func (c *bigQueryClient) GrantDatasetAccess(ctx context.Context, d *Dataset, use return err } -func (c *bigQueryClient) RevokeDatasetAccess(ctx context.Context, d *Dataset, user, role string) error { +func (c *bigQueryClient) RevokeDatasetAccess(ctx context.Context, d *Dataset, accountType, accountID, role string) error { dataset := c.client.Dataset(d.DatasetID) metadata, err := dataset.Metadata(ctx) if err != nil { return err } + entityType := bq.UserEmailEntity + if accountType == AccountTypeGroup { + entityType = bq.GroupEmailEntity + } remainingAccessEntries := []*bq.AccessEntry{} for _, a := range metadata.Access { - if a.Entity == user && string(a.Role) == role { + if a.EntityType == entityType && a.Entity == accountID && string(a.Role) == role { continue } remainingAccessEntries = append(remainingAccessEntries, a) diff --git a/plugins/providers/bigquery/config.go b/plugins/providers/bigquery/config.go index ed9c46eb3..2936a9f75 100644 --- a/plugins/providers/bigquery/config.go +++ b/plugins/providers/bigquery/config.go @@ -21,6 +21,7 @@ const ( AccountTypeUser = "user" AccountTypeServiceAccount = "serviceAccount" + AccountTypeGroup = "group" ) // Credentials is the authentication configuration used by the bigquery client diff --git a/plugins/providers/bigquery/mocks/BigQueryClient.go b/plugins/providers/bigquery/mocks/BigQueryClient.go index 3869e5ce7..ec815fa32 100644 --- a/plugins/providers/bigquery/mocks/BigQueryClient.go +++ b/plugins/providers/bigquery/mocks/BigQueryClient.go @@ -244,13 +244,13 @@ func (_c *BigQueryClient_GetTables_Call) RunAndReturn(run func(context.Context, return _c } -// GrantDatasetAccess provides a mock function with given fields: ctx, d, user, role -func (_m *BigQueryClient) GrantDatasetAccess(ctx context.Context, d *bigquery.Dataset, user string, role string) error { - ret := _m.Called(ctx, d, user, role) +// GrantDatasetAccess provides a mock function with given fields: ctx, d, accountType, accountID, role +func (_m *BigQueryClient) GrantDatasetAccess(ctx context.Context, d *bigquery.Dataset, accountType string, accountID string, role string) error { + ret := _m.Called(ctx, d, accountType, accountID, role) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *bigquery.Dataset, string, string) error); ok { - r0 = rf(ctx, d, user, role) + if rf, ok := ret.Get(0).(func(context.Context, *bigquery.Dataset, string, string, string) error); ok { + r0 = rf(ctx, d, accountType, accountID, role) } else { r0 = ret.Error(0) } @@ -266,15 +266,16 @@ type BigQueryClient_GrantDatasetAccess_Call struct { // GrantDatasetAccess is a helper method to define mock.On call // - ctx context.Context // - d *bigquery.Dataset -// - user string +// - accountType string +// - accountID string // - role string -func (_e *BigQueryClient_Expecter) GrantDatasetAccess(ctx interface{}, d interface{}, user interface{}, role interface{}) *BigQueryClient_GrantDatasetAccess_Call { - return &BigQueryClient_GrantDatasetAccess_Call{Call: _e.mock.On("GrantDatasetAccess", ctx, d, user, role)} +func (_e *BigQueryClient_Expecter) GrantDatasetAccess(ctx interface{}, d interface{}, accountType interface{}, accountID interface{}, role interface{}) *BigQueryClient_GrantDatasetAccess_Call { + return &BigQueryClient_GrantDatasetAccess_Call{Call: _e.mock.On("GrantDatasetAccess", ctx, d, accountType, accountID, role)} } -func (_c *BigQueryClient_GrantDatasetAccess_Call) Run(run func(ctx context.Context, d *bigquery.Dataset, user string, role string)) *BigQueryClient_GrantDatasetAccess_Call { +func (_c *BigQueryClient_GrantDatasetAccess_Call) Run(run func(ctx context.Context, d *bigquery.Dataset, accountType string, accountID string, role string)) *BigQueryClient_GrantDatasetAccess_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*bigquery.Dataset), args[2].(string), args[3].(string)) + run(args[0].(context.Context), args[1].(*bigquery.Dataset), args[2].(string), args[3].(string), args[4].(string)) }) return _c } @@ -284,7 +285,7 @@ func (_c *BigQueryClient_GrantDatasetAccess_Call) Return(_a0 error) *BigQueryCli return _c } -func (_c *BigQueryClient_GrantDatasetAccess_Call) RunAndReturn(run func(context.Context, *bigquery.Dataset, string, string) error) *BigQueryClient_GrantDatasetAccess_Call { +func (_c *BigQueryClient_GrantDatasetAccess_Call) RunAndReturn(run func(context.Context, *bigquery.Dataset, string, string, string) error) *BigQueryClient_GrantDatasetAccess_Call { _c.Call.Return(run) return _c } @@ -445,13 +446,13 @@ func (_c *BigQueryClient_ListRolePermissions_Call) RunAndReturn(run func(context return _c } -// RevokeDatasetAccess provides a mock function with given fields: ctx, d, user, role -func (_m *BigQueryClient) RevokeDatasetAccess(ctx context.Context, d *bigquery.Dataset, user string, role string) error { - ret := _m.Called(ctx, d, user, role) +// RevokeDatasetAccess provides a mock function with given fields: ctx, d, accountType, accountID, role +func (_m *BigQueryClient) RevokeDatasetAccess(ctx context.Context, d *bigquery.Dataset, accountType string, accountID string, role string) error { + ret := _m.Called(ctx, d, accountType, accountID, role) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *bigquery.Dataset, string, string) error); ok { - r0 = rf(ctx, d, user, role) + if rf, ok := ret.Get(0).(func(context.Context, *bigquery.Dataset, string, string, string) error); ok { + r0 = rf(ctx, d, accountType, accountID, role) } else { r0 = ret.Error(0) } @@ -467,15 +468,16 @@ type BigQueryClient_RevokeDatasetAccess_Call struct { // RevokeDatasetAccess is a helper method to define mock.On call // - ctx context.Context // - d *bigquery.Dataset -// - user string +// - accountType string +// - accountID string // - role string -func (_e *BigQueryClient_Expecter) RevokeDatasetAccess(ctx interface{}, d interface{}, user interface{}, role interface{}) *BigQueryClient_RevokeDatasetAccess_Call { - return &BigQueryClient_RevokeDatasetAccess_Call{Call: _e.mock.On("RevokeDatasetAccess", ctx, d, user, role)} +func (_e *BigQueryClient_Expecter) RevokeDatasetAccess(ctx interface{}, d interface{}, accountType interface{}, accountID interface{}, role interface{}) *BigQueryClient_RevokeDatasetAccess_Call { + return &BigQueryClient_RevokeDatasetAccess_Call{Call: _e.mock.On("RevokeDatasetAccess", ctx, d, accountType, accountID, role)} } -func (_c *BigQueryClient_RevokeDatasetAccess_Call) Run(run func(ctx context.Context, d *bigquery.Dataset, user string, role string)) *BigQueryClient_RevokeDatasetAccess_Call { +func (_c *BigQueryClient_RevokeDatasetAccess_Call) Run(run func(ctx context.Context, d *bigquery.Dataset, accountType string, accountID string, role string)) *BigQueryClient_RevokeDatasetAccess_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*bigquery.Dataset), args[2].(string), args[3].(string)) + run(args[0].(context.Context), args[1].(*bigquery.Dataset), args[2].(string), args[3].(string), args[4].(string)) }) return _c } @@ -485,7 +487,7 @@ func (_c *BigQueryClient_RevokeDatasetAccess_Call) Return(_a0 error) *BigQueryCl return _c } -func (_c *BigQueryClient_RevokeDatasetAccess_Call) RunAndReturn(run func(context.Context, *bigquery.Dataset, string, string) error) *BigQueryClient_RevokeDatasetAccess_Call { +func (_c *BigQueryClient_RevokeDatasetAccess_Call) RunAndReturn(run func(context.Context, *bigquery.Dataset, string, string, string) error) *BigQueryClient_RevokeDatasetAccess_Call { _c.Call.Return(run) return _c } diff --git a/plugins/providers/bigquery/provider.go b/plugins/providers/bigquery/provider.go index 9f19df4d4..3b3578902 100644 --- a/plugins/providers/bigquery/provider.go +++ b/plugins/providers/bigquery/provider.go @@ -47,8 +47,8 @@ var ( type BigQueryClient interface { GetDatasets(context.Context) ([]*Dataset, error) GetTables(ctx context.Context, datasetID string) ([]*Table, error) - GrantDatasetAccess(ctx context.Context, d *Dataset, user, role string) error - RevokeDatasetAccess(ctx context.Context, d *Dataset, user, role string) error + GrantDatasetAccess(ctx context.Context, d *Dataset, accountType, accountID, role string) error + RevokeDatasetAccess(ctx context.Context, d *Dataset, accountType, accountID, role string) error GrantTableAccess(ctx context.Context, t *Table, accountType, accountID, role string) error RevokeTableAccess(ctx context.Context, t *Table, accountType, accountID, role string) error ListAccess(ctx context.Context, resources []*domain.Resource) (domain.MapResourceAccess, error) @@ -222,7 +222,7 @@ func (p *Provider) GrantAccess(ctx context.Context, pc *domain.ProviderConfig, a } for _, p := range permissions { - if err := bqClient.GrantDatasetAccess(ctx, d, a.AccountID, string(p)); err != nil { + if err := bqClient.GrantDatasetAccess(ctx, d, a.AccountType, a.AccountID, string(p)); err != nil { if errors.Is(err, ErrPermissionAlreadyExists) { return nil } @@ -274,7 +274,7 @@ func (p *Provider) RevokeAccess(ctx context.Context, pc *domain.ProviderConfig, } for _, p := range permissions { - if err := bqClient.RevokeDatasetAccess(ctx, d, a.AccountID, string(p)); err != nil { + if err := bqClient.RevokeDatasetAccess(ctx, d, a.AccountType, a.AccountID, string(p)); err != nil { if errors.Is(err, ErrPermissionNotFound) { return nil } @@ -312,6 +312,7 @@ func (p *Provider) GetAccountTypes() []string { return []string{ AccountTypeUser, AccountTypeServiceAccount, + AccountTypeGroup, } } diff --git a/plugins/providers/bigquery/provider_test.go b/plugins/providers/bigquery/provider_test.go index ed17bcb6a..326319bbd 100644 --- a/plugins/providers/bigquery/provider_test.go +++ b/plugins/providers/bigquery/provider_test.go @@ -547,7 +547,9 @@ func TestGrantAccess(t *testing.T) { ServiceAccountKey: base64.StdEncoding.EncodeToString([]byte("service-account-key-json")), ResourceName: "projects/resource-name", } - client.On("GrantDatasetAccess", mock.Anything, mock.Anything, expectedAccountID, mock.Anything).Return(expectedError).Once() + client.EXPECT(). + GrantDatasetAccess(mock.Anything, mock.Anything, expectedAccountType, expectedAccountID, mock.Anything). + Return(expectedError).Once() pc := &domain.ProviderConfig{ Type: "bigquery", @@ -599,7 +601,9 @@ func TestGrantAccess(t *testing.T) { ServiceAccountKey: base64.StdEncoding.EncodeToString([]byte("service-account-key-json")), ResourceName: "projects/resource-name", } - client.On("GrantDatasetAccess", mock.Anything, mock.Anything, expectedAccountID, mock.Anything).Return(bigquery.ErrPermissionAlreadyExists).Once() + client.EXPECT(). + GrantDatasetAccess(mock.Anything, mock.Anything, expectedAccountType, expectedAccountID, mock.Anything). + Return(bigquery.ErrPermissionAlreadyExists).Once() pc := &domain.ProviderConfig{ Type: "bigquery", @@ -807,7 +811,9 @@ func TestRevokeAccess(t *testing.T) { ServiceAccountKey: base64.StdEncoding.EncodeToString([]byte("service-account-key-json")), ResourceName: "projects/resource-name", } - client.On("RevokeDatasetAccess", mock.Anything, mock.Anything, expectedAccountID, mock.Anything).Return(expectedError).Once() + client.EXPECT(). + RevokeDatasetAccess(mock.Anything, mock.Anything, expectedAccountType, expectedAccountID, mock.Anything). + Return(expectedError).Once() pc := &domain.ProviderConfig{ Type: "bigquery", @@ -859,7 +865,9 @@ func TestRevokeAccess(t *testing.T) { ServiceAccountKey: base64.StdEncoding.EncodeToString([]byte("service-account-key-json")), ResourceName: "projects/resource-name", } - client.On("RevokeDatasetAccess", mock.Anything, mock.Anything, expectedAccountID, mock.Anything).Return(bigquery.ErrPermissionNotFound).Once() + client.EXPECT(). + RevokeDatasetAccess(mock.Anything, mock.Anything, expectedAccountType, expectedAccountID, mock.Anything). + Return(bigquery.ErrPermissionNotFound).Once() pc := &domain.ProviderConfig{ Type: "bigquery", @@ -957,7 +965,7 @@ func TestRevokeAccess(t *testing.T) { func TestGetAccountTypes(t *testing.T) { t.Run("should return the supported account types \"user\" and \"serviceAccount\"", func(t *testing.T) { p := initProvider() - expectedAccountTypes := []string{"user", "serviceAccount"} + expectedAccountTypes := []string{"user", "serviceAccount", "group"} actualAccountTypes := p.GetAccountTypes()