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(policy): subject condition sets prune service/db #1688

Merged
merged 8 commits into from
Nov 6, 2024
Merged
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
373 changes: 187 additions & 186 deletions protocol/go/policy/objects.pb.go

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions service/integration/subject_mappings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func (s *SubjectMappingsSuite) SetupSuite() {
c := *Config
c.DB.Schema = "test_opentdf_subject_mappings"
s.db = fixtures.NewDBInterface(c)
s.ctx = context.Background()
s.f = fixtures.NewFixture(s.db)
s.ctx = context.Background()
s.f.Provision()
Expand Down Expand Up @@ -637,6 +638,64 @@ func (s *SubjectMappingsSuite) TestDeleteSubjectConditionSet_WithNonExistentId_F
s.Require().ErrorIs(err, db.ErrNotFound)
}

func (s *SubjectMappingsSuite) TestDeleteAllUnmappedSubjectConditionSets() {
// create two new subject condition sets, create a subject mapping with one of them, then verify only the unmapped is deleted
newSCS := &subjectmapping.SubjectConditionSetCreate{
SubjectSets: []*policy.SubjectSet{
{
ConditionGroups: []*policy.ConditionGroup{
{
Conditions: []*policy.Condition{
{
SubjectExternalSelectorValue: ".some_selector",
},
},
},
},
},
},
}

unmapped, err := s.db.PolicyClient.CreateSubjectConditionSet(s.ctx, newSCS)
s.Require().NoError(err)
s.NotNil(unmapped)

mapped, err := s.db.PolicyClient.CreateSubjectConditionSet(s.ctx, newSCS)
s.Require().NoError(err)
s.NotNil(mapped)

sm, err := s.db.PolicyClient.CreateSubjectMapping(s.ctx, &subjectmapping.CreateSubjectMappingRequest{
AttributeValueId: s.f.GetAttributeValueKey("example.net/attr/attr1/value/value2").ID,
Actions: []*policy.Action{fixtureActions[Decrypt]},
ExistingSubjectConditionSetId: mapped.GetId(),
})
s.Require().NoError(err)
s.NotNil(sm)

deleted, err := s.db.PolicyClient.DeleteAllUnmappedSubjectConditionSets(s.ctx)
s.Require().NoError(err)
s.NotEmpty(deleted)
unmappedDeleted := true
mappedDeleted := false
for _, scs := range deleted {
deletedID := scs.GetId()
if deletedID == unmapped.GetId() {
unmappedDeleted = true
}
if deletedID == mapped.GetId() {
mappedDeleted = true
}
}
s.True(unmappedDeleted)
s.False(mappedDeleted)

// cannot get after pruning
got, err := s.db.PolicyClient.GetSubjectConditionSet(s.ctx, unmapped.GetId())
s.Nil(got)
s.Require().Error(err)
s.ErrorIs(err, db.ErrNotFound)
}

func (s *SubjectMappingsSuite) TestUpdateSubjectConditionSet_NewSubjectSets() {
// create a new one, update nothing but the subject sets, and verify the solo update
newConditionSet := &subjectmapping.SubjectConditionSetCreate{
Expand Down
5 changes: 5 additions & 0 deletions service/policy/db/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,11 @@ WHERE id = $1;
-- name: DeleteSubjectConditionSet :execrows
DELETE FROM subject_condition_set WHERE id = $1;

-- name: DeleteAllUnmappedSubjectConditionSets :many
DELETE FROM subject_condition_set
WHERE id NOT IN (SELECT DISTINCT sm.subject_condition_set_id FROM subject_mappings sm)
RETURNING id;

----------------------------------------------------------------
-- SUBJECT MAPPINGS
----------------------------------------------------------------
Expand Down
31 changes: 31 additions & 0 deletions service/policy/db/query.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions service/policy/db/subject_mappings.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,25 @@ func (c PolicyDBClient) DeleteSubjectConditionSet(ctx context.Context, id string
}, nil
}

// Deletes/prunes all subject condition sets not referenced within a subject mapping
func (c PolicyDBClient) DeleteAllUnmappedSubjectConditionSets(ctx context.Context) ([]*policy.SubjectConditionSet, error) {
deletedIDs, err := c.Queries.DeleteAllUnmappedSubjectConditionSets(ctx)
if err != nil {
return nil, db.WrapIfKnownInvalidQueryErr(err)
}
if len(deletedIDs) == 0 {
return nil, db.ErrNotFound
}

setList := make([]*policy.SubjectConditionSet, len(deletedIDs))
for i, id := range deletedIDs {
setList[i] = &policy.SubjectConditionSet{
Id: id,
}
}
return setList, nil
}

/*
Subject Mappings
*/
Expand Down
4 changes: 2 additions & 2 deletions service/policy/objects.proto
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ message SubjectMapping {
*/
message Condition {
// a selector for a field value on a flattened Entity Representation (such as from idP/LDAP)
string subject_external_selector_value = 1;
string subject_external_selector_value = 1 [ (buf.validate.field).required = true ];

// the evaluation operator of relation
SubjectMappingOperatorEnum operator = 2 [
Expand All @@ -163,7 +163,7 @@ message Condition {
];

// list of comparison values for the result of applying the subject_external_selector_value on a flattened Entity Representation (Subject), evaluated by the operator
repeated string subject_external_values = 3;
repeated string subject_external_values = 3 [(buf.validate.field).repeated.min_items = 1];
}

// A collection of Conditions evaluated by the boolean_operator provided
Expand Down
27 changes: 27 additions & 0 deletions service/policy/subjectmapping/subject_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,30 @@ func (s SubjectMappingService) DeleteSubjectConditionSet(ctx context.Context,
}
return rsp, nil
}

func (s SubjectMappingService) DeleteAllUnmappedSubjectConditionSets(ctx context.Context,
_ *sm.DeleteAllUnmappedSubjectConditionSetsRequest,
) (*sm.DeleteAllUnmappedSubjectConditionSetsResponse, error) {
rsp := &sm.DeleteAllUnmappedSubjectConditionSetsResponse{}
s.logger.Debug("deleting all unmapped subject condition sets")

auditParams := audit.PolicyEventParams{
ActionType: audit.ActionTypeDelete,
ObjectType: audit.ObjectTypeConditionSet,
}

deleted, err := s.dbClient.DeleteAllUnmappedSubjectConditionSets(ctx)
if err != nil {
s.logger.Audit.PolicyCRUDFailure(ctx, auditParams)
return nil, db.StatusifyError(err, db.ErrTextDeletionFailed)
}

// Log each pruned subject condition set to audit
for _, scs := range deleted {
auditParams.ObjectID = scs.GetId()
s.logger.Audit.PolicyCRUDSuccess(ctx, auditParams)
}

rsp.SubjectConditionSets = deleted
return rsp, nil
}
Loading