diff --git a/pkg/cmd/validate.go b/pkg/cmd/validate.go index b74060c7a..d1edb1bc5 100644 --- a/pkg/cmd/validate.go +++ b/pkg/cmd/validate.go @@ -235,6 +235,85 @@ func validate() func(cmd *cobra.Command, args []string) error { // Start log output for checks color.Notice.Println(" checks:") + //Iterate over all the scenarios specific relationships + var createdLocalRelationshipsTups []*base.Tuple + for _, t := range scenario.Relationships { + // Convert each 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 + } + + createdLocalRelationshipsTups = append(createdLocalRelationshipsTups, tup) + // If the tuple was successfully written to the database, log a success message + color.Success.Println(fmt.Sprintf(" scenario relationship success: %s ", t)) + } + + //Iterate over all the scenarios specific relationship + var createdScenarioAttributes []*base.Attribute + for _, a := range s.Attributes { + // Convert each 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 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 + } + + createdScenarioAttributes = append(createdScenarioAttributes, attr) + // If the attribute was successfully written to the database, log a success message + color.Success.Println(fmt.Sprintf(" scenario attribute success: %s ", a)) + } // Iterate over all checks in the scenario for _, check := range scenario.Checks { // Extract entity from the check @@ -439,6 +518,46 @@ func validate() func(cmd *cobra.Command, args []string) error { } } } + + // Once scenarios are tested delete created local scenario specific relationships + for _, t := range createdLocalRelationshipsTups { + // Write the relationship to the database + _, err = dev.Container.DW.Delete(ctx, "t1", &base.TupleFilter{ + Entity: &base.EntityFilter{ + Type: t.GetEntity().Type, + Ids: []string{t.Entity.Id}, + }, + Relation: t.GetRelation(), + }, + &base.AttributeFilter{}, + ) + // Continue to the next relationship if an error occurred + if err != nil { + // If an error occurs, add it to the list and continue to the next filter. + list.Add(err.Error()) + continue + } + } + + // Once scenarios are tested delete created local scenario specific attributes + for _, a := range createdScenarioAttributes { + // Write the relationship to the database + _, err = dev.Container.DW.Delete(ctx, "t1", &base.TupleFilter{}, + &base.AttributeFilter{ + Entity: &base.EntityFilter{ + Type: a.GetEntity().Type, + Ids: []string{a.Entity.Id}, + }, + Attributes: []string{a.GetAttribute()}, + }, + ) + // Continue to the next relationship if an error occurred + if err != nil { + // If an error occurs, add it to the list and continue to the next filter. + list.Add(err.Error()) + continue + } + } } // If the error list is not empty, there were some errors during processing. diff --git a/pkg/development/development.go b/pkg/development/development.go index 246c87abf..b02d609f5 100644 --- a/pkg/development/development.go +++ b/pkg/development/development.go @@ -277,6 +277,103 @@ 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 { + var createdScenarioRelationshipsTups []*v1.Tuple + // Add scenario specific local relationships + 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 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 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 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 + } + createdScenarioRelationshipsTups = append(createdScenarioRelationshipsTups, tup) + } + + // Add scenario specific attributes + var createdScenarioAttributes []*v1.Attribute + 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 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 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 + } + createdScenarioAttributes = append(createdScenarioAttributes, attr) + } // Each Check in the current scenario is processed for _, check := range scenario.Checks { entity, err := tuple.E(check.Entity) @@ -519,6 +616,54 @@ func (c *Development) Run(ctx context.Context, shape map[string]interface{}) (er } } } + + // Once the scenarios are tested, delete created scenario specific relationships. + for _, t := range createdScenarioRelationshipsTups { + // Write the relationship to the database + _, err = c.Container.DW.Delete(ctx, "t1", &v1.TupleFilter{ + Entity: &v1.EntityFilter{ + Type: t.GetEntity().Type, + Ids: []string{t.Entity.Id}, + }, + Relation: t.GetRelation(), + }, + &v1.AttributeFilter{}, + ) + // Continue to the next relationship if an error occurred + if err != nil { + // If an error occurs, add it to the list and continue to the next filter. + errors = append(errors, Error{ + Type: "scenarios", + Key: i, + Message: err.Error(), + }) + continue + } + } + + // Once the scenarios are tested, delete created scenario specific attributes. + for _, a := range createdScenarioAttributes { + // Write the relationship to the database + _, err = c.Container.DW.Delete(ctx, "t1", &v1.TupleFilter{}, + &v1.AttributeFilter{ + Entity: &v1.EntityFilter{ + Type: a.GetEntity().Type, + Ids: []string{a.Entity.Id}, + }, + Attributes: []string{a.GetAttribute()}, + }, + ) + // Continue to the next relationship if an error occurred + if err != nil { + // If an error occurs, add it to the list and continue to the next filter. + errors = append(errors, Error{ + Type: "scenarios", + Key: i, + Message: err.Error(), + }) + continue + } + } } return diff --git a/pkg/development/file/shape.go b/pkg/development/file/shape.go index 5d86bd767..1c071324a 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"`