From 5641af670dff2fb95434b8c8a96480e00a275088 Mon Sep 17 00:00:00 2001 From: Aazam Thakur <59562284+aazam-gh@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:55:06 +0000 Subject: [PATCH] feat: integrate scenario-specific relationships & attributes --- pkg/cmd/validate.go | 76 +++++++++++++++ pkg/development/coverage/coverage.go | 41 +++++--- pkg/development/development.go | 94 +++++++++++++++++++ pkg/development/file/shape.go | 6 ++ .../layout/components/Modals/newScenario.js | 2 + 5 files changed, 208 insertions(+), 11 deletions(-) diff --git a/pkg/cmd/validate.go b/pkg/cmd/validate.go index b74060c7a..dd1f4b30f 100644 --- a/pkg/cmd/validate.go +++ b/pkg/cmd/validate.go @@ -232,6 +232,82 @@ func validate() func(cmd *cobra.Command, args []string) error { for sn, scenario := range s.Scenarios { color.Notice.Printf("%v.scenario: %s - %s\n", sn+1, scenario.Name, scenario.Description) + // Iterate over all scenario relationships in the subject + for _, t := range scenario.Relationships { + // Convert each scenario relationship to a Tuple + var tup *base.Tuple + tup, err = tuple.Tuple(t) + // If an error occurs during the conversion, add the error message to the list and continue to the next iteration + if err != nil { + list.Add(err.Error()) + continue + } + + // Retrieve the entity definition associated with the tuple's entity type + definition, _, err := dev.Container.SR.ReadEntityDefinition(ctx, "t1", tup.GetEntity().GetType(), version) + // If an error occurs while reading the entity definition, return the error + if err != nil { + return err + } + + // Validate the tuple using the entity definition + err = serverValidation.ValidateTuple(definition, tup) + // If an error occurs during validation, return the error + if err != nil { + return err + } + + // Write the validated tuple to the database + _, err = dev.Container.DW.Write(ctx, "t1", database.NewTupleCollection(tup), database.NewAttributeCollection()) + // If an error occurs while writing to the database, add an error message to the list, log the error and continue to the next iteration + if err != nil { + list.Add(fmt.Sprintf("%s failed %s", t, err.Error())) + color.Danger.Println(fmt.Sprintf("fail: %s failed %s", t, validationError(err.Error()))) + continue + } + + // If the tuple was successfully written to the database, log a success message + color.Success.Println(fmt.Sprintf(" success: %s ", t)) + } + + // Iterate over all scenario attributes in the subject + for _, a := range scenario.Attributes { + // Convert each scnario attribute to an Attribute + var attr *base.Attribute + attr, err = attribute.Attribute(a) + // If an error occurs during the conversion, add the error message to the list and continue to the next iteration + if err != nil { + list.Add(err.Error()) + continue + } + + // Retrieve the entity definition associated with the attribute's entity type + definition, _, err := dev.Container.SR.ReadEntityDefinition(ctx, "t1", attr.GetEntity().GetType(), version) + // If an error occurs while reading the entity definition, return the error + if err != nil { + return err + } + + // Validate the scenario attribute using the entity definition + err = serverValidation.ValidateAttribute(definition, attr) + // If an error occurs during validation, return the error + if err != nil { + return err + } + + // Write the validated attribute to the database + _, err = dev.Container.DW.Write(ctx, "t1", database.NewTupleCollection(), database.NewAttributeCollection(attr)) + // If an error occurs while writing to the database, add an error message to the list, log the error and continue to the next iteration + if err != nil { + list.Add(fmt.Sprintf("%s failed %s", a, err.Error())) + color.Danger.Println(fmt.Sprintf("fail: %s failed %s", a, validationError(err.Error()))) + continue + } + + // If the attribute was successfully written to the database, log a success message + color.Success.Println(fmt.Sprintf(" success: %s ", a)) + } + // Start log output for checks color.Notice.Println(" checks:") diff --git a/pkg/development/coverage/coverage.go b/pkg/development/coverage/coverage.go index a55ad7243..32b6e11e9 100644 --- a/pkg/development/coverage/coverage.go +++ b/pkg/development/coverage/coverage.go @@ -118,11 +118,6 @@ func Run(shape file.Shape) SchemaCoverageInfo { } } - entityCoverageInfo.CoverageRelationshipsPercent = calculateCoveragePercent( - ref.Relationships, - entityCoverageInfo.UncoveredRelationships, - ) - // Calculate attributes coverage at := attributes(ref.EntityName, shape.Attributes) @@ -132,13 +127,37 @@ func Run(shape file.Shape) SchemaCoverageInfo { } } - entityCoverageInfo.CoverageAttributesPercent = calculateCoveragePercent( - ref.Attributes, - entityCoverageInfo.UncoveredAttributes, - ) - - // Calculate assertions coverage for each scenario + // Calculate relationships, attributes and assertions coverage for each scenario for _, s := range shape.Scenarios { + + // Calculate relationships coverage + er := relationships(ref.EntityName, shape.Relationships) + + for _, relationship := range ref.Relationships { + if !slices.Contains(er, relationship) { + entityCoverageInfo.UncoveredRelationships = append(entityCoverageInfo.UncoveredRelationships, relationship) + } + } + + entityCoverageInfo.CoverageRelationshipsPercent = calculateCoveragePercent( + ref.Relationships, + entityCoverageInfo.UncoveredRelationships, + ) + + // Calculate attributes coverage + at := attributes(ref.EntityName, s.Attributes) + + for _, attr := range ref.Attributes { + if !slices.Contains(at, attr) { + entityCoverageInfo.UncoveredAttributes = append(entityCoverageInfo.UncoveredAttributes, attr) + } + } + + entityCoverageInfo.CoverageAttributesPercent = calculateCoveragePercent( + ref.Attributes, + entityCoverageInfo.UncoveredAttributes, + ) + ca := assertions(ref.EntityName, s.Checks, s.EntityFilters) for _, assertion := range ref.Assertions { diff --git a/pkg/development/development.go b/pkg/development/development.go index 5d1e24d7f..b8feb814a 100644 --- a/pkg/development/development.go +++ b/pkg/development/development.go @@ -281,6 +281,100 @@ func (c *Development) Run(ctx context.Context, shape map[string]interface{}) (er // Each item in the Scenarios slice is processed individually for i, scenario := range s.Scenarios { + // Each item in the Scenario Relationships slice is processed individually + for _, t := range scenario.Relationships { + tup, err := tuple.Tuple(t) + if err != nil { + errors = append(errors, Error{ + Type: "relationships", + Key: t, + Message: err.Error(), + }) + continue + } + + // Read the schema definition for this scenario relationship + definition, _, err := c.Container.SR.ReadEntityDefinition(ctx, "t1", tup.GetEntity().GetType(), version) + if err != nil { + errors = append(errors, Error{ + Type: "relationships", + Key: t, + Message: err.Error(), + }) + continue + } + + // Validate the scenario relationship tuple against the schema definition + err = validation.ValidateTuple(definition, tup) + if err != nil { + errors = append(errors, Error{ + Type: "relationships", + Key: t, + Message: err.Error(), + }) + continue + } + + // Write the scenario relationship to the database + _, err = c.Container.DW.Write(ctx, "t1", database.NewTupleCollection(tup), database.NewAttributeCollection()) + // Continue to the next relationship if an error occurred + if err != nil { + errors = append(errors, Error{ + Type: "relationships", + Key: t, + Message: err.Error(), + }) + continue + } + } + + // Each item in the Scenario Attributes slice is processed individually + for _, a := range scenario.Attributes { + attr, err := attribute.Attribute(a) + if err != nil { + errors = append(errors, Error{ + Type: "attributes", + Key: a, + Message: err.Error(), + }) + continue + } + + // Read the schema definition for this scenario attribute + definition, _, err := c.Container.SR.ReadEntityDefinition(ctx, "t1", attr.GetEntity().GetType(), version) + if err != nil { + errors = append(errors, Error{ + Type: "attributes", + Key: a, + Message: err.Error(), + }) + continue + } + + // Validate the attribute against the schema definition + err = validation.ValidateAttribute(definition, attr) + if err != nil { + errors = append(errors, Error{ + Type: "attributes", + Key: a, + Message: err.Error(), + }) + continue + } + + // Write the scenario attribute to the database + _, err = c.Container.DW.Write(ctx, "t1", database.NewTupleCollection(), database.NewAttributeCollection(attr)) + // Continue to the next attribute if an error occurred + if err != nil { + errors = append(errors, Error{ + Type: "attributes", + Key: a, + Message: err.Error(), + }) + continue + } + } + // Each Check in the current scenario is processed for _, check := range scenario.Checks { entity, err := tuple.E(check.Entity) diff --git a/pkg/development/file/shape.go b/pkg/development/file/shape.go index 420e42525..306e3fce0 100644 --- a/pkg/development/file/shape.go +++ b/pkg/development/file/shape.go @@ -23,6 +23,12 @@ type Scenario struct { // Description is a string that provides a brief explanation of the scenario. Description string `yaml:"description"` + // Scenario specific local Relationships is slice of strings that represent the authorization relationships. + Relationships []string `yaml:"relationships"` + + // Scenario specific local attributes is a slice of strings that represent the authorization attributes. + Attributes []string `yaml:"attributes"` + // Checks is a slice of Check structs that represent the authorization checks to be performed. Checks []Check `yaml:"checks"` diff --git a/playground/src/layout/components/Modals/newScenario.js b/playground/src/layout/components/Modals/newScenario.js index 0376d773a..c68233608 100644 --- a/playground/src/layout/components/Modals/newScenario.js +++ b/playground/src/layout/components/Modals/newScenario.js @@ -23,6 +23,8 @@ function NewScenario(props) { assertions: {} } ], + relationships: [], + attributes: [], entity_filters: [], subject_filters: [] };