diff --git a/pkg/persistence/account/errors.go b/pkg/persistence/account/errors.go new file mode 100644 index 000000000..4f4fe4471 --- /dev/null +++ b/pkg/persistence/account/errors.go @@ -0,0 +1,23 @@ +/* + * @license + * Copyright 2023 Dynatrace LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package account + +import "errors" + +var ErrRefMissing = errors.New("no referenced target found") +var ErrIdFieldMissing = errors.New("no ref id field found") +var ErrIdFieldNoString = errors.New("ref id field is not a string") diff --git a/pkg/persistence/account/test-resources/no-id-field-group-ref.yaml b/pkg/persistence/account/test-resources/no-id-field-group-ref.yaml new file mode 100644 index 000000000..3e7d1f758 --- /dev/null +++ b/pkg/persistence/account/test-resources/no-id-field-group-ref.yaml @@ -0,0 +1,5 @@ +users: + - email: monaco@dynatrace.com + groups: + - type: reference + - Log viewer diff --git a/pkg/persistence/account/test-resources/no-ref-group.yaml b/pkg/persistence/account/test-resources/no-ref-group.yaml new file mode 100644 index 000000000..40b0aebcb --- /dev/null +++ b/pkg/persistence/account/test-resources/no-ref-group.yaml @@ -0,0 +1,17 @@ +users: + - email: monaco@dynatrace.com + groups: + - type: reference + id: non-existing-group-ref + - Default Group + +# GROUPS +groups: + - name: Another Group + id: another-group + description: This is a group + account: + permissions: + - Default permissions + policies: + - Default policies diff --git a/pkg/persistence/account/test-resources/no-ref-policy-account.yaml b/pkg/persistence/account/test-resources/no-ref-policy-account.yaml new file mode 100644 index 000000000..fb3872e75 --- /dev/null +++ b/pkg/persistence/account/test-resources/no-ref-policy-account.yaml @@ -0,0 +1,14 @@ +users: + - email: monaco@dynatrace.com + groups: + - type: reference + id: my-group +# GROUPS +groups: + - name: My Group + id: my-group + description: This is my group + account: + policies: + - type: reference + id: non-existing-policy-ref diff --git a/pkg/persistence/account/test-resources/no-ref-policy-env.yaml b/pkg/persistence/account/test-resources/no-ref-policy-env.yaml new file mode 100644 index 000000000..8963d7d73 --- /dev/null +++ b/pkg/persistence/account/test-resources/no-ref-policy-env.yaml @@ -0,0 +1,18 @@ +users: + - email: monaco@dynatrace.com + groups: + - type: reference + id: my-group +# GROUPS +groups: + - name: My Group + id: my-group + description: This is my group + account: + environment: + - name: myenv + permissions: + - View environment + policies: + - type: reference + id: non-existing-policy-ref diff --git a/pkg/persistence/account/test-resources/valid.yaml b/pkg/persistence/account/test-resources/valid.yaml new file mode 100644 index 000000000..fde64aff9 --- /dev/null +++ b/pkg/persistence/account/test-resources/valid.yaml @@ -0,0 +1,42 @@ +users: + - email: monaco@dynatrace.com + groups: + - type: reference + id: my-group + - Log viewer + + +groups: + - name: My Group + id: my-group + description: This is my group + account: + permissions: + - View my Group Stuff + policies: + - Request My Group Stuff + + environment: + - name: myenv123 + permissions: + - View environment + policies: + - View environment + - type: reference + id: my-policy + + managementZone: + - environment: env12345 + managementZone: Mzone + permissions: + - View environment + +policies: + - name: My Policy + id: my-policy + level: + type: account + description: abcde + policy: |- + ALLOW a:b:c; + diff --git a/pkg/persistence/account/types.go b/pkg/persistence/account/types.go index 3062f5925..cc1573da0 100644 --- a/pkg/persistence/account/types.go +++ b/pkg/persistence/account/types.go @@ -73,21 +73,3 @@ type ( Groups []any `mapstructure:"groups"` } ) - -func (a *AMResources) GroupExists(id string) bool { - for _, g := range a.Groups { - if g.ID == id { - return true - } - } - return false -} - -func (a *AMResources) PolicyExists(id string) bool { - for _, p := range a.Policies { - if p.ID == id { - return true - } - } - return false -} diff --git a/pkg/persistence/account/validate.go b/pkg/persistence/account/validate.go new file mode 100644 index 000000000..dd93f8184 --- /dev/null +++ b/pkg/persistence/account/validate.go @@ -0,0 +1,86 @@ +/* + * @license + * Copyright 2023 Dynatrace LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package account + +import "fmt" + +// Validate checks the references in the provided AMResources instance to ensure +// that all referenced groups and policies exist. It iterates through the users, +// environment policies, and account policies, validating their references. +func Validate(res *AMResources) error { + for _, user := range res.Users { + for _, groupRef := range user.Groups { + if err := refCheck(groupRef, res.GroupExists); err != nil { + return err + } + } + } + + for _, group := range res.Groups { + // check references in environment policies + for _, env := range group.Environment { + for _, policyRef := range env.Policies { + if err := refCheck(policyRef, res.PolicyExists); err != nil { + return err + } + } + } + // check references in account policies + for _, policyRef := range group.Account.Policies { + if err := refCheck(policyRef, res.PolicyExists); err != nil { + return err + } + } + } + return nil +} + +func refCheck(elem any, refCheckFn func(string) bool) error { + if reference, isCustomRef := elem.(anyMap); isCustomRef { + idField, hasIdField := reference["id"] + if !hasIdField { + return fmt.Errorf("error validating account resources: %w", ErrIdFieldMissing) + } + idFieldStr, isIdFieldStr := idField.(string) + if !isIdFieldStr { + return fmt.Errorf("error validating account resources: %w", ErrIdFieldNoString) + } + policyRefExists := refCheckFn(idFieldStr) + if !policyRefExists { + return fmt.Errorf("error validating account resources with id %q: %w", idFieldStr, ErrRefMissing) + } + } + return nil +} + +func (a *AMResources) GroupExists(id string) bool { + for _, g := range a.Groups { + if g.ID == id { + return true + } + } + return false +} + +func (a *AMResources) PolicyExists(id string) bool { + for _, p := range a.Policies { + if p.ID == id { + return true + } + } + return false +} diff --git a/pkg/persistence/account/validate_test.go b/pkg/persistence/account/validate_test.go new file mode 100644 index 000000000..bc888800e --- /dev/null +++ b/pkg/persistence/account/validate_test.go @@ -0,0 +1,76 @@ +/* + * @license + * Copyright 2023 Dynatrace LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package account + +import ( + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestValidateT(t *testing.T) { + testCases := []struct { + name string + path string + expected error + expectedErrMsg string + }{ + { + + name: "group reference not found", + path: "test-resources/no-ref-group.yaml", + expected: ErrRefMissing, + expectedErrMsg: `error validating account resources with id "non-existing-group-ref": no referenced target found`, + }, + { + name: "environment level policy reference not found", + path: "test-resources/no-ref-policy-env.yaml", + expected: ErrRefMissing, + expectedErrMsg: `error validating account resources with id "non-existing-policy-ref": no referenced target found`, + }, + { + name: "account level policy reference not found", + path: "test-resources/no-ref-policy-account.yaml", + expected: ErrRefMissing, + expectedErrMsg: `error validating account resources with id "non-existing-policy-ref": no referenced target found`, + }, + { + name: "group reference with missing id field", + path: "test-resources/no-id-field-group-ref.yaml", + expected: ErrIdFieldMissing, + expectedErrMsg: `error validating account resources: no ref id field found`, + }, + { + name: "valid", + path: "test-resources/valid.yaml", + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resources, _ := Load(afero.NewOsFs(), tc.path) + err := Validate(resources) + if tc.expected != nil { + assert.ErrorIs(t, err, tc.expected) + assert.ErrorContains(t, err, tc.expectedErrMsg) + } else { + assert.NoError(t, err) + } + }) + } +}