Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
9337910
feat(policy): limit/offset throughout LIST RPCs/db
jakedoublev Oct 17, 2024
6c35f18
protos limit/offset
jakedoublev Oct 17, 2024
727fa85
Revert "feat(policy): limit/offset throughout LIST RPCs/db"
jakedoublev Oct 17, 2024
850b26e
update sqlc queries with limit/offset
jakedoublev Oct 17, 2024
18edecc
put back
jakedoublev Oct 17, 2024
69e74ac
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 17, 2024
fd05a46
default policy object list count
jakedoublev Oct 17, 2024
1c7cdb1
cleanup and ensure attribute protos included
jakedoublev Oct 17, 2024
68b7c31
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 17, 2024
42ec2ba
use sqlc named params for LIST queries in db layer to allow build
jakedoublev Oct 17, 2024
1f4eeb5
add next_offset to protos
jakedoublev Oct 17, 2024
3813980
response proto pagination fields
jakedoublev Oct 17, 2024
6720ae5
Merge branch 'main' into feat/limit-offset
jakedoublev Oct 17, 2024
c608cd2
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 17, 2024
a3f393a
DRY up list proto pagination
jakedoublev Oct 18, 2024
4cbddc8
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 18, 2024
0c1ca0f
request pagination from service to DB
jakedoublev Oct 18, 2024
792c567
update signatures to take in list requests to db handlers with pagina…
jakedoublev Oct 18, 2024
23c9330
get total within SQL
jakedoublev Oct 18, 2024
07c0031
kasr list db handlers with next offset, total, current offset
jakedoublev Oct 21, 2024
b0ce1eb
handle total/nextOffset throughout list responses
jakedoublev Oct 22, 2024
29c6e1b
cleanup and listAll functionality
jakedoublev Oct 22, 2024
4a0080a
do not expose db states outside db package, update integration tests,…
jakedoublev Oct 22, 2024
782dfbd
tests will at least run
jakedoublev Oct 22, 2024
bbab4ea
tests all pass but are ineffective
jakedoublev Oct 22, 2024
a3e2be3
deprecate leftover members fixture struct type
jakedoublev Oct 22, 2024
2c50983
ensure list all actually lists all state objects
jakedoublev Oct 22, 2024
d50e69e
rm unnecessary group by
jakedoublev Oct 22, 2024
f9a81fe
values pagination integration tests
jakedoublev Oct 22, 2024
f77affd
tests
jakedoublev Oct 24, 2024
89a8422
add config and docs for policy default limit and max
jakedoublev Oct 24, 2024
8bd713e
update key of policy config
jakedoublev Oct 24, 2024
6e992cd
list limit/max read into policy services' config
jakedoublev Oct 24, 2024
1f4331f
handle max limit validation in services
jakedoublev Oct 24, 2024
cfcba52
finish reading configured default/max into tests, fixtures, and db li…
jakedoublev Oct 24, 2024
2b17fb3
fix the reindex cmd
jakedoublev Oct 24, 2024
9a198a5
put back config docs autofmt
jakedoublev Oct 24, 2024
87705d1
push list limit validation down into db layer
jakedoublev Oct 24, 2024
45f829b
cleanup
jakedoublev Oct 24, 2024
04e638d
improve validation and panic errors for policy config
jakedoublev Oct 24, 2024
f7e2d9d
working tests
jakedoublev Oct 24, 2024
e13430d
max limit tests
jakedoublev Oct 24, 2024
551e711
wrap tests
jakedoublev Oct 24, 2024
6f36824
Merge branch 'main' into feat/limit-offset
jakedoublev Oct 24, 2024
d0bde6d
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 24, 2024
e94eba9
lint fixes
jakedoublev Oct 25, 2024
2287a25
fix proto comments
jakedoublev Oct 25, 2024
dd5e012
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 25, 2024
23c365a
Merge branch 'main' into feat/limit-offset
jakedoublev Oct 30, 2024
b9d8142
Merge branch 'feat/limit-offset' into feat/limit-offset-svc
jakedoublev Oct 30, 2024
bf7f694
bump protocol/go
jakedoublev Nov 5, 2024
ac0c73f
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
ecf9402
merge conflict
jakedoublev Nov 5, 2024
be8c6a9
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
22561d8
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
81cfc17
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
8f2a9e9
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
f2fafe2
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
7d97117
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
534fdd8
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 5, 2024
316dddb
Merge branch 'main' into feat/limit-offset-svc
jakedoublev Nov 19, 2024
3a5bcaa
fix missed type change
jakedoublev Nov 19, 2024
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
18 changes: 18 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,24 @@ services:
query: data.opentdf.entitlements.attributes
```

### Policy

Root level key `policy`

| Field | Description | Default | Environment Variables |
| ---------------------------- | ------------------------------------------------------ | ------- | -------------------------------------------------- |
| `list_request_limit_default` | Policy List request limit default when not provided | 1000 | OPENTDF_SERVICES_POLICY_LIST_REQUEST_LIMIT_DEFAULT |
| `list_request_limit_max` | Policy List request limit maximum enforced by services | 2500 | OPENTDF_SERVICES_POLICY_LIST_REQUEST_LIMIT_MAX |

Example:

```yaml
services:
policy:
list_request_limit_default: 1000
list_request_limit_max: 2500
```

### Casbin Endpoint Authorization

OpenTDF uses Casbin to manage authorization policies. This document provides an overview of how to configure and manage the default authorization policy in OpenTDF.
Expand Down
5 changes: 5 additions & 0 deletions opentdf-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ services:
from:
email: true
username: true
# policy is enabled by default in mode 'all'
# policy:
# enabled: true
# list_request_limit_default: 1000
# list_request_limit_max: 2500
server:
tls:
enabled: false
Expand Down
5 changes: 5 additions & 0 deletions opentdf-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ services:
from:
email: true
username: true
# policy is enabled by default in mode 'all'
# policy:
# enabled: true
# list_request_limit_default: 1000
# list_request_limit_max: 2500
server:
auth:
enabled: true
Expand Down
2 changes: 2 additions & 0 deletions opentdf-with-hsm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ services:
rsacertid: r1
policy:
enabled: true
# list_request_limit_default: 1000
# list_request_limit_max: 2500
entityresolution:
enabled: true
url: http://localhost:8888/auth
Expand Down
8 changes: 7 additions & 1 deletion service/cmd/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ func policyDBClient(conf *config.Config) (policydb.PolicyDBClient, error) {
return policydb.PolicyDBClient{}, err
}

return policydb.NewClient(dbClient, logger), nil
// This command connects directly to the database so runtime policy config list limit settings can be ignored
var (
limitDefault int32 = 1000
limitMax int32 = 2500
)

return policydb.NewClient(dbClient, logger, limitMax, limitDefault), nil
}

func init() {
Expand Down
191 changes: 157 additions & 34 deletions service/integration/attribute_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"github.com/opentdf/platform/protocol/go/policy/unsafe"
"github.com/opentdf/platform/service/internal/fixtures"
"github.com/opentdf/platform/service/pkg/db"
policydb "github.com/opentdf/platform/service/policy/db"
"github.com/stretchr/testify/suite"
"google.golang.org/protobuf/proto"
)

var absentAttributeValueUUID = "78909865-8888-9999-9999-000000000000"
Expand Down Expand Up @@ -44,30 +44,143 @@ func (s *AttributeValuesSuite) TearDownSuite() {
s.f.TearDown()
}

func (s *AttributeValuesSuite) Test_ListAttributeValues() {
func (s *AttributeValuesSuite) Test_ListAttributeValues_WithAttributeID_Succeeds() {
attrID := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value1").AttributeDefinitionID

list, err := s.db.PolicyClient.ListAttributeValues(s.ctx, attrID, policydb.StateActive)
listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
AttributeId: attrID,
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE,
})
s.Require().NoError(err)
s.NotNil(list)
s.NotNil(listRsp)
listed := listRsp.GetValues()

// ensure list contains the two test fixtures and that response matches expected data
f1 := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value1")
f2 := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value2")

for _, item := range list {
if item.GetId() == f1.ID {
s.Equal(f1.ID, item.GetId())
s.Equal(f1.Value, item.GetValue())
// s.Equal(f1.AttributeDefinitionId, item.AttributeId)
} else if item.GetId() == f2.ID {
s.Equal(f2.ID, item.GetId())
s.Equal(f2.Value, item.GetValue())
// s.Equal(f2.AttributeDefinitionId, item.AttributeId)
for _, val := range listed {
if val.GetId() == f1.ID {
s.Equal(f1.ID, val.GetId())
s.Equal(f1.Value, val.GetValue())
s.Equal(f1.AttributeDefinitionID, val.GetAttribute().GetId())
} else if val.GetId() == f2.ID {
s.Equal(f2.ID, val.GetId())
s.Equal(f2.Value, val.GetValue())
s.Equal(f2.AttributeDefinitionID, val.GetAttribute().GetId())
}
}
}

func (s *AttributeValuesSuite) Test_ListAttributeValues_NoPagination_Succeeds() {
allFixtureValueFqns := map[string]bool{
"https://example.com/attr/attr1/value/value1": false,
"https://example.com/attr/attr1/value/value2": false,
"https://example.com/attr/attr2/value/value1": false,
"https://example.com/attr/attr2/value/value2": false,
"https://example.net/attr/attr1/value/value1": false,
"https://example.net/attr/attr1/value/value2": false,
"https://scenario.com/attr/working_group/value/blue": false,
"https://deactivated.io/attr/deactivated_attr/value/deactivated_value": false,
}
listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
})
s.Require().NoError(err)
s.NotNil(listRsp)
// mark every listed value true
for _, val := range listRsp.GetValues() {
allFixtureValueFqns[val.GetFqn()] = true
}
// ensure all fixtures were found by unbounded list
for fqn, found := range allFixtureValueFqns {
if !found {
s.Failf("failed to list fixture", fqn)
}
}
}

func (s *AttributeValuesSuite) Test_ListAttributeValues_Limit_Succeeds() {
var limit int32 = 2
listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
Pagination: &policy.PageRequest{
Limit: limit,
},
})
s.Require().NoError(err)
s.NotNil(listRsp)
listed := listRsp.GetValues()
s.Equal(len(listed), int(limit))

for _, val := range listed {
s.NotEmpty(val.GetFqn())
s.NotEmpty(val.GetId())
s.NotEmpty(val.GetValue())
}

// request with one below maximum
listRsp, err = s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
Pagination: &policy.PageRequest{
Limit: s.db.LimitMax - 1,
},
})
s.Require().NoError(err)
s.NotNil(listRsp)

// request with exactly maximum
listRsp, err = s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
Pagination: &policy.PageRequest{
Limit: s.db.LimitMax,
},
})
s.Require().NoError(err)
s.NotNil(listRsp)
}

func (s *NamespacesSuite) Test_ListAttributeValues_Limit_TooLarge_Fails() {
listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
Pagination: &policy.PageRequest{
Limit: s.db.LimitMax + 1,
},
})
s.Require().Error(err)
s.Require().ErrorIs(err, db.ErrListLimitTooLarge)
s.Nil(listRsp)
}

func (s *AttributeValuesSuite) Test_ListAttributeValues_Offset_Succeeds() {
req := &attributes.ListAttributeValuesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
}
// make initial list request to compare against
listRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, req)
s.Require().NoError(err)
s.NotNil(listRsp)
listed := listRsp.GetValues()

// set the offset pagination
offset := 5
req.Pagination = &policy.PageRequest{
Offset: int32(offset),
}
offsetListRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, req)
s.Require().NoError(err)
s.NotNil(offsetListRsp)
offsetListed := offsetListRsp.GetValues()

// length is reduced by the offset amount
s.Equal(len(offsetListed), len(listed)-offset)

// objects are equal between offset and original list beginning at offset index
for i, val := range offsetListed {
s.True(proto.Equal(val, listed[i+offset]))
}
}

func (s *AttributeValuesSuite) Test_GetAttributeValue() {
f := s.f.GetAttributeValueKey("example.com/attr/attr1/value/value1")
v, err := s.db.PolicyClient.GetAttributeValue(s.ctx, f.ID)
Expand Down Expand Up @@ -133,7 +246,7 @@ func (s *AttributeValuesSuite) Test_CreateAttributeValue_SetsActiveStateTrueByDe
attrDef := s.f.GetAttributeKey("example.net/attr/attr1")

req := &attributes.CreateAttributeValueRequest{
Value: "testing create gives active true by default",
Value: "testing-create-gives-active-true-by-default",
}
createdValue, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attrDef.ID, req)
s.Require().NoError(err)
Expand Down Expand Up @@ -495,15 +608,18 @@ func setupDeactivateAttributeValue(s *AttributeValuesSuite) (string, string, str
func (s *AttributeValuesSuite) Test_DeactivateAttribute_Cascades_List() {
type test struct {
name string
testFunc func(state string) bool
state string
testFunc func(state common.ActiveStateEnum) bool
state common.ActiveStateEnum
isFound bool
}

listNamespaces := func(state string) bool {
listedNamespaces, err := s.db.PolicyClient.ListNamespaces(s.ctx, state)
listNamespaces := func(state common.ActiveStateEnum) bool {
listedNamespacesRsp, err := s.db.PolicyClient.ListNamespaces(s.ctx, &namespaces.ListNamespacesRequest{
State: state,
})
s.Require().NoError(err)
s.NotNil(listedNamespaces)
s.NotNil(listedNamespacesRsp)
listedNamespaces := listedNamespacesRsp.GetNamespaces()
for _, ns := range listedNamespaces {
if stillActiveNsID == ns.GetId() {
return true
Expand All @@ -512,10 +628,13 @@ func (s *AttributeValuesSuite) Test_DeactivateAttribute_Cascades_List() {
return false
}

listAttributes := func(state string) bool {
listedAttrs, err := s.db.PolicyClient.ListAttributes(s.ctx, state, "")
listAttributes := func(state common.ActiveStateEnum) bool {
listedAttrsRsp, err := s.db.PolicyClient.ListAttributes(s.ctx, &attributes.ListAttributesRequest{
State: state,
})
s.Require().NoError(err)
s.NotNil(listedAttrs)
s.NotNil(listedAttrsRsp)
listedAttrs := listedAttrsRsp.GetAttributes()
for _, a := range listedAttrs {
if stillActiveAttributeID == a.GetId() {
return true
Expand All @@ -524,10 +643,14 @@ func (s *AttributeValuesSuite) Test_DeactivateAttribute_Cascades_List() {
return false
}

listValues := func(state string) bool {
listedVals, err := s.db.PolicyClient.ListAttributeValues(s.ctx, stillActiveAttributeID, state)
listValues := func(state common.ActiveStateEnum) bool {
listedValsRsp, err := s.db.PolicyClient.ListAttributeValues(s.ctx, &attributes.ListAttributeValuesRequest{
State: state,
AttributeId: stillActiveAttributeID,
})
s.Require().NoError(err)
s.NotNil(listedVals)
s.NotNil(listedValsRsp)
listedVals := listedValsRsp.GetValues()
for _, v := range listedVals {
if deactivatedAttrValueID == v.GetId() {
return true
Expand All @@ -540,55 +663,55 @@ func (s *AttributeValuesSuite) Test_DeactivateAttribute_Cascades_List() {
{
name: "namespace is NOT found in LIST of INACTIVE",
testFunc: listNamespaces,
state: policydb.StateInactive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_INACTIVE,
isFound: false,
},
{
name: "namespace is found when filtering for ACTIVE state",
testFunc: listNamespaces,
state: policydb.StateActive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE,
isFound: true,
},
{
name: "namespace is found when filtering for ANY state",
testFunc: listNamespaces,
state: policydb.StateAny,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
isFound: true,
},
{
name: "attribute is NOT found when filtering for INACTIVE state",
testFunc: listAttributes,
state: policydb.StateInactive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_INACTIVE,
isFound: false,
},
{
name: "attribute is found when filtering for ANY state",
testFunc: listAttributes,
state: policydb.StateAny,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
isFound: true,
},
{
name: "attribute is found when filtering for ACTIVE state",
testFunc: listAttributes,
state: policydb.StateActive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE,
isFound: true,
},
{
name: "value is NOT found in LIST of ACTIVE",
testFunc: listValues,
state: policydb.StateActive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ACTIVE,
isFound: false,
},
{
name: "value is found when filtering for INACTIVE state",
testFunc: listValues,
state: policydb.StateInactive,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_INACTIVE,
isFound: true,
},
{
name: "value is found when filtering for ANY state",
testFunc: listValues,
state: policydb.StateAny,
state: common.ActiveStateEnum_ACTIVE_STATE_ENUM_ANY,
isFound: true,
},
}
Expand Down
Loading
Loading