Skip to content

Commit

Permalink
Validate Data Sources when creating rules. (#5068)
Browse files Browse the repository at this point in the history
Rules can now specify a list of data sources names that they need for
their evaluation. This change ensures that data sources required by
the rule are available when creating the rule.

A similar change is being added in the code path implementing data
source deletion ensuring that a data source is not deleted if there
exist rules depending on it.

Fixes #5049
  • Loading branch information
blkt authored Nov 28, 2024
1 parent 37cd413 commit e71d4d9
Show file tree
Hide file tree
Showing 3 changed files with 416 additions and 30 deletions.
54 changes: 44 additions & 10 deletions internal/controlplane/handlers_ruletype.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

datasourcesvc "github.com/mindersec/minder/internal/datasources/service"
"github.com/mindersec/minder/internal/db"
"github.com/mindersec/minder/internal/engine/engcontext"
"github.com/mindersec/minder/internal/flags"
Expand Down Expand Up @@ -175,12 +176,13 @@ func (s *Server) CreateRuleType(
return nil, util.UserVisibleError(codes.InvalidArgument, "%s", err)
}

ds := crt.GetRuleType().GetDef().GetEval().GetDataSources()
if len(ds) > 0 && !flags.Bool(ctx, s.featureFlags, flags.DataSources) {
return nil, status.Errorf(codes.Unavailable, "DataSources feature is disabled")
}

newRuleType, err := db.WithTransaction(s.store, func(qtx db.ExtendQuerier) (*minderv1.RuleType, error) {
ruleDS := crt.GetRuleType().GetDef().GetEval().GetDataSources()
if err := s.validateDataSources(ctx, projectID, ruleDS, qtx); err != nil {
// We expect the error to be a user visible error
return nil, err
}

return s.ruleTypes.CreateRuleType(ctx, projectID, uuid.Nil, crt.GetRuleType(), qtx)
})
if err != nil {
Expand Down Expand Up @@ -220,12 +222,13 @@ func (s *Server) UpdateRuleType(
return nil, util.UserVisibleError(codes.InvalidArgument, "%s", err)
}

ds := urt.GetRuleType().GetDef().GetEval().GetDataSources()
if len(ds) > 0 && !flags.Bool(ctx, s.featureFlags, flags.DataSources) {
return nil, status.Errorf(codes.Unavailable, "DataSources feature is disabled")
}

updatedRuleType, err := db.WithTransaction(s.store, func(qtx db.ExtendQuerier) (*minderv1.RuleType, error) {
ruleDS := urt.GetRuleType().GetDef().GetEval().GetDataSources()
if err := s.validateDataSources(ctx, projectID, ruleDS, qtx); err != nil {
// We expect the error to be a user visible error
return nil, err
}

return s.ruleTypes.UpdateRuleType(ctx, projectID, uuid.Nil, urt.GetRuleType(), qtx)
})
if err != nil {
Expand Down Expand Up @@ -373,3 +376,34 @@ func validateMarkdown(md string) error {

return nil
}

func (s *Server) validateDataSources(
ctx context.Context,
projectID uuid.UUID,
ruleDS []*minderv1.DataSourceReference,
qtx db.ExtendQuerier,
) error {
// Short circuiting to avoid accessing the database.
if len(ruleDS) == 0 {
return nil
}

if len(ruleDS) > 0 && !flags.Bool(ctx, s.featureFlags, flags.DataSources) {
return status.Errorf(codes.Unavailable, "DataSources feature is disabled")
}

opts := datasourcesvc.ReadBuilder().WithTransaction(qtx)
for _, requested := range ruleDS {
_, err := s.dataSourcesService.GetByName(
ctx,
requested.Name,
projectID,
opts,
)
if err != nil {
return util.UserVisibleError(codes.Internal, "failed retrieving data sources")
}
}

return nil
}
Loading

0 comments on commit e71d4d9

Please sign in to comment.