Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate scenario-specific relationships and attributes #1113

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions pkg/cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:")

Expand Down
41 changes: 30 additions & 11 deletions pkg/development/coverage/coverage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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 {
Expand Down
94 changes: 94 additions & 0 deletions pkg/development/development.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions pkg/development/file/shape.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`

Expand Down
2 changes: 2 additions & 0 deletions playground/src/layout/components/Modals/newScenario.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ function NewScenario(props) {
assertions: {}
}
],
relationships: [],
attributes: [],
entity_filters: [],
subject_filters: []
};
Expand Down