Skip to content

Commit e098032

Browse files
committed
feat(policy): cache SubjectConditionSet selectors in dedicated column maintained via trigger (#2320)
### Proposed Changes * Maintain a cache of selectors utilized for the `MatchSubjectMappings` RPC to power fetching resolve-able entitlement objects * Improve protovalidate implementation of subject mappings/subject condition sets creation * Add a new db integration test for `MatchSubjectMappings` that ensures updation to the selectors ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions
1 parent 2fe716f commit e098032

File tree

12 files changed

+858
-267
lines changed

12 files changed

+858
-267
lines changed

protocol/go/policy/subjectmapping/subject_mapping.pb.go

Lines changed: 195 additions & 194 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

service/integration/subject_mappings_test.go

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,22 @@ func (s *SubjectMappingsSuite) Test_ListSubjectConditionSet_Offset_Succeeds() {
958958
func (s *SubjectMappingsSuite) TestDeleteSubjectConditionSet() {
959959
// create a new subject condition set, delete it, and verify get fails with not found
960960
newConditionSet := &subjectmapping.SubjectConditionSetCreate{
961-
SubjectSets: []*policy.SubjectSet{},
961+
SubjectSets: []*policy.SubjectSet{
962+
{
963+
ConditionGroups: []*policy.ConditionGroup{
964+
{
965+
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_OR,
966+
Conditions: []*policy.Condition{
967+
{
968+
SubjectExternalSelectorValue: ".someField[1]",
969+
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN,
970+
SubjectExternalValues: []string{"some_value"},
971+
},
972+
},
973+
},
974+
},
975+
},
976+
},
962977
}
963978

964979
created, err := s.db.PolicyClient.CreateSubjectConditionSet(s.ctx, newConditionSet)
@@ -1575,6 +1590,113 @@ func (s *SubjectMappingsSuite) TestGetMatchedSubjectMappings_NonExistentField_Re
15751590
s.Empty(sm)
15761591
}
15771592

1593+
func (s *SubjectMappingsSuite) TestGetMatchedSubjectMappings_ResponsiveToUpdation() {
1594+
// Create a Subject Condition Set with a specific selector
1595+
initialSelector := ".test_updation_selector"
1596+
updatedSelector := ".updated_selector" // Will be used later for the update
1597+
1598+
subjectConditionSet := &subjectmapping.SubjectConditionSetCreate{
1599+
SubjectSets: []*policy.SubjectSet{
1600+
{
1601+
ConditionGroups: []*policy.ConditionGroup{
1602+
{
1603+
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
1604+
Conditions: []*policy.Condition{
1605+
{
1606+
SubjectExternalSelectorValue: initialSelector,
1607+
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
1608+
SubjectExternalValues: []string{"test_value"},
1609+
},
1610+
},
1611+
},
1612+
},
1613+
},
1614+
},
1615+
}
1616+
1617+
createdSCS, err := s.db.PolicyClient.CreateSubjectConditionSet(s.ctx, subjectConditionSet)
1618+
s.Require().NoError(err)
1619+
s.NotNil(createdSCS)
1620+
1621+
// Create a Subject Mapping with the created SCS
1622+
fixtureAttrValID := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value2").ID
1623+
actionRead := s.f.GetStandardAction(policydb.ActionRead.String())
1624+
1625+
subjectMapping := &subjectmapping.CreateSubjectMappingRequest{
1626+
AttributeValueId: fixtureAttrValID,
1627+
Actions: []*policy.Action{actionRead},
1628+
ExistingSubjectConditionSetId: createdSCS.GetId(),
1629+
}
1630+
1631+
createdSM, err := s.db.PolicyClient.CreateSubjectMapping(s.ctx, subjectMapping)
1632+
s.Require().NoError(err)
1633+
s.NotNil(createdSM)
1634+
1635+
// Validate the subject mapping is matched using the initial selector but not updated
1636+
props := []*policy.SubjectProperty{
1637+
{
1638+
ExternalSelectorValue: initialSelector,
1639+
},
1640+
}
1641+
1642+
matchedList, err := s.db.PolicyClient.GetMatchedSubjectMappings(s.ctx, props)
1643+
s.Require().NoError(err)
1644+
s.Len(matchedList, 1)
1645+
1646+
matchedSM := matchedList[0]
1647+
s.Equal(createdSM.GetId(), matchedSM.GetId())
1648+
1649+
updatedProps := []*policy.SubjectProperty{
1650+
{
1651+
ExternalSelectorValue: updatedSelector, // This selector is not yet in use
1652+
},
1653+
}
1654+
1655+
matchedList, err = s.db.PolicyClient.GetMatchedSubjectMappings(s.ctx, updatedProps)
1656+
s.Require().NoError(err)
1657+
s.Empty(matchedList)
1658+
1659+
// Update the Subject Condition Set with a different selector
1660+
updateRequest := &subjectmapping.UpdateSubjectConditionSetRequest{
1661+
Id: createdSCS.GetId(),
1662+
SubjectSets: []*policy.SubjectSet{
1663+
{
1664+
ConditionGroups: []*policy.ConditionGroup{
1665+
{
1666+
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
1667+
Conditions: []*policy.Condition{
1668+
{
1669+
SubjectExternalSelectorValue: updatedSelector, // Changed selector
1670+
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
1671+
SubjectExternalValues: []string{"test_value"},
1672+
},
1673+
},
1674+
},
1675+
},
1676+
},
1677+
},
1678+
}
1679+
1680+
updatedSCS, err := s.db.PolicyClient.UpdateSubjectConditionSet(s.ctx, updateRequest)
1681+
s.Require().NoError(err)
1682+
s.NotNil(updatedSCS)
1683+
1684+
matchedAfterUpdate, err := s.db.PolicyClient.GetMatchedSubjectMappings(s.ctx, props)
1685+
s.Require().NoError(err)
1686+
s.Empty(matchedAfterUpdate)
1687+
1688+
matchedList, err = s.db.PolicyClient.GetMatchedSubjectMappings(s.ctx, updatedProps)
1689+
s.Require().NoError(err)
1690+
s.Len(matchedList, 1)
1691+
1692+
matchedSM = matchedList[0]
1693+
s.Equal(createdSM.GetId(), matchedSM.GetId())
1694+
1695+
matchedList, err = s.db.PolicyClient.GetMatchedSubjectMappings(s.ctx, props)
1696+
s.Require().NoError(err)
1697+
s.Empty(matchedList)
1698+
}
1699+
15781700
func (s *SubjectMappingsSuite) TestUpdateSubjectConditionSet_MetadataVariations() {
15791701
fixedLabel := "fixed label"
15801702
updateLabel := "update label"

service/internal/fixtures/fixtures.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ type SubjectConditionSet struct {
6464
Condition struct {
6565
SubjectSets []struct {
6666
ConditionGroups []struct {
67-
BooleanOperator string `yaml:"boolean_operator" json:"boolean_operator"`
67+
BooleanOperator string `yaml:"booleanOperator" json:"booleanOperator"`
6868
Conditions []struct {
69-
SubjectExternalSelectorValue string `yaml:"subject_external_selector_value" json:"subject_external_selector_value"`
69+
SubjectExternalSelectorValue string `yaml:"subjectExternalSelectorValue" json:"subjectExternalSelectorValue"`
7070
Operator string `yaml:"operator" json:"operator"`
71-
SubjectExternalValues []string `yaml:"subject_external_values" json:"subject_external_values"`
71+
SubjectExternalValues []string `yaml:"subjectExternalValues" json:"subjectExternalValues"`
7272
} `yaml:"conditions" json:"conditions"`
73-
} `yaml:"condition_groups" json:"condition_groups"`
74-
} `yaml:"subject_sets" json:"subject_sets"`
73+
} `yaml:"conditionGroups" json:"conditionGroups"`
74+
} `yaml:"subjectSets" json:"subjectSets"`
7575
} `yaml:"condition" json:"condition"`
7676
}
7777

service/internal/fixtures/policy_fixtures.yaml

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -245,116 +245,116 @@ subject_condition_set:
245245
subject_condition_set1:
246246
id: b3903282-06f9-41a4-924a-7b8eb43dffe0
247247
condition:
248-
subject_sets:
249-
- condition_groups:
250-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_AND
248+
subjectSets:
249+
- conditionGroups:
250+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_AND
251251
conditions:
252-
- subject_external_selector_value: ".attributes.superhero_name[]"
252+
- subjectExternalSelectorValue: ".attributes.superhero_name[]"
253253
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
254-
subject_external_values:
254+
subjectExternalValues:
255255
- thor
256256
- captain_america
257-
- subject_external_selector_value: ".attributes.superhero_group[]"
257+
- subjectExternalSelectorValue: ".attributes.superhero_group[]"
258258
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
259-
subject_external_values:
259+
subjectExternalValues:
260260
- avengers
261261
subject_condition_set2:
262262
id: 798aacd2-abaf-4623-975e-3bb8ca43e318
263263
condition:
264-
subject_sets:
265-
- condition_groups:
266-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_AND
264+
subjectSets:
265+
- conditionGroups:
266+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_AND
267267
conditions:
268-
- subject_external_selector_value: ".org"
268+
- subjectExternalSelectorValue: ".org"
269269
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
270-
subject_external_values:
270+
subjectExternalValues:
271271
- marketing
272272
- sales
273-
- subject_external_selector_value: ".role"
273+
- subjectExternalSelectorValue: ".role"
274274
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
275-
subject_external_values:
275+
subjectExternalValues:
276276
- senior_vice_president
277277
- vice_president
278278
- director
279279
subject_condition_set3:
280280
id: eaf866c0-327f-4826-846a-5041c3c22f06
281281
condition:
282-
subject_sets:
283-
- condition_groups:
284-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_OR
282+
subjectSets:
283+
- conditionGroups:
284+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_OR
285285
conditions:
286286
# any index
287-
- subject_external_selector_value: ".data[0].favorite_things[]"
287+
- subjectExternalSelectorValue: ".data[0].favorite_things[]"
288288
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
289-
subject_external_values:
289+
subjectExternalValues:
290290
- futbol
291291
- soccer
292292
# specific index
293-
- subject_external_selector_value: ".data[0].favorite_things[1]"
293+
- subjectExternalSelectorValue: ".data[0].favorite_things[1]"
294294
operator: SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN
295-
subject_external_values:
295+
subjectExternalValues:
296296
- ice_cream
297-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_AND
297+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_AND
298298
conditions:
299-
- subject_external_selector_value: ".department"
299+
- subjectExternalSelectorValue: ".department"
300300
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
301-
subject_external_values:
301+
subjectExternalValues:
302302
- engineering
303-
- subject_external_selector_value: ".role"
303+
- subjectExternalSelectorValue: ".role"
304304
operator: SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN
305-
subject_external_values:
305+
subjectExternalValues:
306306
- manager
307307
- director
308308
- vice_president
309309
subject_condition_simple_in:
310310
id: 3c623ede-df88-4906-8a78-ebdfacadcd57
311311
condition:
312-
subject_sets:
313-
- condition_groups:
314-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_OR
312+
subjectSets:
313+
- conditionGroups:
314+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_OR
315315
conditions:
316-
- subject_external_selector_value: ".some_field"
316+
- subjectExternalSelectorValue: ".some_field"
317317
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
318-
subject_external_values:
318+
subjectExternalValues:
319319
- some_value
320320
subject_condition_simple_not_in:
321321
id: cf17ec4c-d206-4b74-b3db-5ce07d6995cc
322322
condition:
323-
subject_sets:
324-
- condition_groups:
325-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_OR
323+
subjectSets:
324+
- conditionGroups:
325+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_OR
326326
conditions:
327-
- subject_external_selector_value: ".some_other_field[1]"
327+
- subjectExternalSelectorValue: ".some_other_field[1]"
328328
operator: SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN
329-
subject_external_values:
329+
subjectExternalValues:
330330
- some_other_value_123
331331
subject_condition_working_group_blue_scenario:
332332
id: 10d03422-7eae-43b9-ac3b-d10400171858
333333
condition:
334-
subject_sets:
335-
- condition_groups:
336-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_AND
334+
subjectSets:
335+
- conditionGroups:
336+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_AND
337337
conditions:
338-
- subject_external_selector_value: ".team.name"
338+
- subjectExternalSelectorValue: ".team.name"
339339
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
340-
subject_external_values:
340+
subjectExternalValues:
341341
- CoolTool
342342
- RadService
343343
- ShinyThing
344-
- subject_external_selector_value: ".org.name"
344+
- subjectExternalSelectorValue: ".org.name"
345345
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
346-
subject_external_values:
346+
subjectExternalValues:
347347
- marketing
348348
subject_condition_sdk_client:
349349
id: 86621a00-b63e-42e9-bea5-40ba52d98ede
350350
condition:
351-
subject_sets:
352-
- condition_groups:
353-
- boolean_operator: CONDITION_BOOLEAN_TYPE_ENUM_OR
351+
subjectSets:
352+
- conditionGroups:
353+
- booleanOperator: CONDITION_BOOLEAN_TYPE_ENUM_OR
354354
conditions:
355-
- subject_external_selector_value: ".clientId"
355+
- subjectExternalSelectorValue: ".clientId"
356356
operator: SUBJECT_MAPPING_OPERATOR_ENUM_IN
357-
subject_external_values:
357+
subjectExternalValues:
358358
- opentdf-sdk
359359

360360
##
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
### Changes
2+
3+
#### Cached selectors approach
4+
5+
Utilize a trigger to set and maintain cached selectors in a dedicated column on the
6+
Subject Condition Set table, then do an overlap `&&` check against the cache instead
7+
of parsing JSON in the `matchSubjectMappings` query.
8+
9+
#### Indices
10+
11+
Index on any relations in the `matchSubjectMappings` query for fastest reads.

0 commit comments

Comments
 (0)