Skip to content
9 changes: 9 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,7 @@ github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcK
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8=
github.com/container-orchestrated-devices/container-device-interface v0.5.4/go.mod h1:DjE95rfPiiSmG7uVXtg0z6MnPm/Lx4wxKCIts0ZE0vg=
github.com/container-orchestrated-devices/container-device-interface v0.6.1 h1:mz77uJoP8im/4Zins+mPqt677ZMaflhoGaYrRAl5jvA=
github.com/container-orchestrated-devices/container-device-interface v0.6.1/go.mod h1:40T6oW59rFrL/ksiSs7q45GzjGlbvxnA4xaK6cyq+kA=
Expand Down Expand Up @@ -609,12 +610,14 @@ github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue
github.com/containerd/imgcrypt v1.1.4/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo=
github.com/containerd/imgcrypt v1.1.7 h1:WSf9o9EQ0KGHiUx2ESFZ+PKf4nxK9BcvV/nJDX8RkB4=
github.com/containerd/imgcrypt v1.1.7/go.mod h1:FD8gqIcX5aTotCtOmjeCsi3A1dHmTZpnMISGKSczt4k=
github.com/containerd/imgcrypt v1.1.8 h1:ZS7TuywcRNLoHpU0g+v4/PsKynl6TYlw5xDVWWoIyFA=
github.com/containerd/imgcrypt v1.1.8/go.mod h1:x6QvFIkMyO2qGIY2zXc88ivEzcbgvLdWjoZyGqDap5U=
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/nri v0.6.0 h1:hdztxwL0gCS1CrCa9bvD1SoJiFN4jBuRQhplCvCPMj8=
github.com/containerd/nri v0.6.0/go.mod h1:F7OZfO4QTPqw5r87aq+syZJwiVvRYLIlHZiZDBV1W3A=
github.com/containerd/nri v0.6.1 h1:xSQ6elnQ4Ynidm9u49ARK9wRKHs80HCUI+bkXOxV4mA=
github.com/containerd/nri v0.6.1/go.mod h1:7+sX3wNx+LR7RzhjnJiUkFDhn18P5Bg/0VnJ/uXpRJM=
github.com/containerd/protobuild v0.3.0 h1:RIyEIu+D+iIha6E1PREBPAXspSMFaDVam81JlolZWpg=
github.com/containerd/protobuild v0.3.0/go.mod h1:5mNMFKKAwCIAkFBPiOdtRx2KiQlyEJeMXnL5R1DsWu8=
Expand All @@ -630,6 +633,7 @@ github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Ev
github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak=
github.com/containerd/ttrpc v1.2.3 h1:4jlhbXIGvijRtNC8F/5CpuJZ7yKOBFGFOOXg1bkISz0=
github.com/containerd/ttrpc v1.2.3/go.mod h1:ieWsXucbb8Mj9PH0rXCw1i8IunRbbAiDkpXkbfflWBM=
github.com/containerd/ttrpc v1.2.4 h1:eQCQK4h9dxDmpOb9QOOMh2NHTfzroH1IkmHiKZi05Oo=
github.com/containerd/ttrpc v1.2.4/go.mod h1:ojvb8SJBSch0XkqNO0L0YX/5NxR3UnVk2LzFKBK0upc=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
Expand Down Expand Up @@ -665,6 +669,7 @@ github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B
github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pABH85425Es2g=
github.com/containers/ocicrypt v1.1.6 h1:uoG52u2e91RE4UqmBICZY8dNshgfvkdl3BW6jnxiFaI=
github.com/containers/ocicrypt v1.1.6/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc=
github.com/containers/ocicrypt v1.1.10 h1:r7UR6o8+lyhkEywetubUUgcKFjOWOaWz8cEBrCPX0ic=
github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down Expand Up @@ -826,6 +831,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
Expand Down Expand Up @@ -1520,6 +1526,7 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -2476,7 +2483,9 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
tags.cncf.io/container-device-interface v0.6.2 h1:dThE6dtp/93ZDGhqaED2Pu374SOeUkBfuvkLuiTdwzg=
tags.cncf.io/container-device-interface v0.6.2/go.mod h1:Shusyhjs1A5Na/kqPVLL0KqnHQHuunol9LFeUNkuGVE=
tags.cncf.io/container-device-interface v0.7.2 h1:MLqGnWfOr1wB7m08ieI4YJ3IoLKKozEnnNYBtacDPQU=
tags.cncf.io/container-device-interface v0.7.2/go.mod h1:Xb1PvXv2BhfNb3tla4r9JL129ck1Lxv9KuU6eVOfKto=
tags.cncf.io/container-device-interface/specs-go v0.6.0 h1:V+tJJN6dqu8Vym6p+Ru+K5mJ49WL6Aoc5SJFSY0RLsQ=
tags.cncf.io/container-device-interface/specs-go v0.6.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
tags.cncf.io/container-device-interface/specs-go v0.7.0 h1:w/maMGVeLP6TIQJVYT5pbqTi8SCw/iHZ+n4ignuGHqg=
tags.cncf.io/container-device-interface/specs-go v0.7.0/go.mod h1:hMAwAbMZyBLdmYqWgYcKH0F/yctNpV3P35f+/088A80=
18 changes: 13 additions & 5 deletions sdk/granter.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,17 +181,20 @@ func (r granter) addGrant(fqn AttributeValueFQN, kas string, attr *policy.Attrib
}
}

func (r granter) addAllGrants(fqn AttributeValueFQN, gs []*policy.KeyAccessServer, attr *policy.Attribute) {
func (r granter) addAllGrants(fqn AttributeValueFQN, gs []*policy.KeyAccessServer, attr *policy.Attribute) bool {
ok := false
for _, g := range gs {
if g != nil {
r.addGrant(fqn, g.GetUri(), attr)
ok = true
}
}
if len(gs) == 0 {
if _, ok := r.grants[fqn.key]; !ok {
if _, present := r.grants[fqn.key]; !present {
r.grants[fqn.key] = &keyAccessGrant{attr, []string{}}
}
}
return ok
}

func (r granter) byAttribute(fqn AttributeValueFQN) *keyAccessGrant {
Expand Down Expand Up @@ -226,15 +229,20 @@ func newGranterFromService(ctx context.Context, keyCache *kasKeyCache, as attrib
return grants, err
}
def := pair.GetAttribute()

if def != nil {
grants.addAllGrants(fqn, def.GetGrants(), def)
storeKeysToCache(def.GetGrants(), keyCache)
}
v := pair.GetValue()
valuesGranted := false
if v != nil {
grants.addAllGrants(fqn, v.GetGrants(), def)
valuesGranted = grants.addAllGrants(fqn, v.GetGrants(), def)
storeKeysToCache(v.GetGrants(), keyCache)
}
// If no more specific grant was found, then add the value grants
if !valuesGranted && def != nil {
grants.addAllGrants(fqn, def.GetGrants(), def)
}
}

return grants, nil
Expand Down Expand Up @@ -272,7 +280,7 @@ func storeKeysToCache(kases []*policy.KeyAccessServer, c *kasKeyCache) {

// Given a policy (list of data attributes or tags),
// get a set of grants from attribute values to KASes.
// Unlike `NewGranterFromService`, this works offline.
// Unlike `newGranterFromService`, this works offline.
func newGranterFromAttributes(attrs ...*policy.Value) (granter, error) {
grants := granter{
grants: make(map[string]*keyAccessGrant),
Expand Down
176 changes: 164 additions & 12 deletions sdk/granter_test.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package sdk

import (
"context"
"fmt"
"regexp"
"strings"
"testing"

"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/attributes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
"google.golang.org/grpc"
)

const (
kasAu = "https://kas.au/"
kasCa = "https://kas.ca/"
kasUk = "https://kas.uk/"
kasNz = "https://kas.nz/"
kasUs = "https://kas.us/"
kasUsHCS = "https://hcs.kas.us/"
kasUsSA = "https://si.kas.us/"
authority = "https://virtru.com/"
kasAu = "https://kas.au/"
kasCa = "https://kas.ca/"
kasUk = "https://kas.uk/"
kasNz = "https://kas.nz/"
kasUs = "https://kas.us/"
kasUsHCS = "https://hcs.kas.us/"
kasUsSA = "https://si.kas.us/"
authority = "https://virtru.com/"
otherAuth = "https://other.com/"
specifiedKas = "https://attr.kas.com/"
evenMoreSpecificKas = "https://value.kas.com/"
)

var (
Expand All @@ -43,6 +49,14 @@ var (
rel2gbr, _ = NewAttributeValueFQN("https://virtru.com/attr/Releasable%20To/value/GBR")
rel2nzl, _ = NewAttributeValueFQN("https://virtru.com/attr/Releasable%20To/value/NZL")
rel2usa, _ = NewAttributeValueFQN("https://virtru.com/attr/Releasable%20To/value/USA")

// attributes to test specificity of kas grants
UNSPECKED, _ = NewAttributeNameFQN("https://other.com/attr/unspecified")
SPECKED, _ = NewAttributeNameFQN("https://other.com/attr/specified")
uns2uns, _ = NewAttributeValueFQN("https://other.com/attr/unspecified/value/unspecked")
uns2spk, _ = NewAttributeValueFQN("https://other.com/attr/unspecified/value/specked")
spk2uns, _ = NewAttributeValueFQN("https://other.com/attr/specified/value/unspecked")
spk2spk, _ = NewAttributeValueFQN("https://other.com/attr/specified/value/specked")
)

func spongeCase(s string) string {
Expand Down Expand Up @@ -85,36 +99,60 @@ func messUpV(t *testing.T, a AttributeValueFQN) AttributeValueFQN {
}

func mockAttributeFor(fqn AttributeNameFQN) *policy.Attribute {
ns := policy.Namespace{
nsOne := policy.Namespace{
Id: "v",
Name: "virtru.com",
Fqn: "https://virtru.com",
}
nsTwo := policy.Namespace{
Id: "o",
Name: "other.com",
Fqn: "https://other.com",
}
switch fqn.key {
case CLS.key:
return &policy.Attribute{
Id: "CLS",
Namespace: &ns,
Namespace: &nsOne,
Name: "Classification",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY,
Fqn: fqn.String(),
}
case N2K.key:
return &policy.Attribute{
Id: "N2K",
Namespace: &ns,
Namespace: &nsOne,
Name: "Need to Know",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF,
Fqn: fqn.String(),
}
case REL.key:
return &policy.Attribute{
Id: "REL",
Namespace: &ns,
Namespace: &nsOne,
Name: "Releasable To",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Fqn: fqn.String(),
}
case SPECKED.key:
g := make([]*policy.KeyAccessServer, 1)
g[0] = &policy.KeyAccessServer{Uri: specifiedKas}
return &policy.Attribute{
Id: "SPK",
Namespace: &nsTwo,
Name: "specified",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Fqn: fqn.String(),
Grants: g,
}
case UNSPECKED.key:
return &policy.Attribute{
Id: "UNS",
Namespace: &nsTwo,
Name: "unspecified",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Fqn: fqn.String(),
}
}
return nil
}
Expand Down Expand Up @@ -171,6 +209,13 @@ func mockValueFor(fqn AttributeValueFQN) *policy.Value {
}
case CLS.key:
// defaults only
case SPECKED.key:
fallthrough
case UNSPECKED.key:
if strings.ToLower(fqn.Value()) == "specked" {
p.Grants = make([]*policy.KeyAccessServer, 1)
p.Grants[0] = &policy.KeyAccessServer{Uri: evenMoreSpecificKas}
}
}
return &p
}
Expand Down Expand Up @@ -397,3 +442,110 @@ func TestReasonerConstructAttributeBoolean(t *testing.T) {
})
}
}

var (
listAttributeResp attributes.ListAttributesResponse
)

type mockAttributesClient struct {
attributes.AttributesServiceClient
}

func (*mockAttributesClient) ListAttributes(_ context.Context, _ *attributes.ListAttributesRequest, _ ...grpc.CallOption) (*attributes.ListAttributesResponse, error) {
return &listAttributeResp, nil
}

func (*mockAttributesClient) GetAttributeValuesByFqns(_ context.Context, req *attributes.GetAttributeValuesByFqnsRequest, _ ...grpc.CallOption) (*attributes.GetAttributeValuesByFqnsResponse, error) {
av := make(map[string]*attributes.GetAttributeValuesByFqnsResponse_AttributeAndValue)
for _, v := range req.GetFqns() {
vfqn, err := NewAttributeValueFQN(v)
if err != nil {
return nil, err
}
val := mockValueFor(vfqn)
av[v] = &attributes.GetAttributeValuesByFqnsResponse_AttributeAndValue{
Attribute: val.GetAttribute(),
Value: val,
}
}

return &attributes.GetAttributeValuesByFqnsResponse{
FqnAttributeValues: av,
}, nil
}

func TestReasonerSpecificity(t *testing.T) {
for _, tc := range []struct {
n string
policy []AttributeValueFQN
defaults []string
plan []keySplitStep
}{
{
"uns.uns => default",
[]AttributeValueFQN{uns2uns},
[]string{kasUs},
[]keySplitStep{{kasUs, ""}},
},
{
"uns.spk => spk",
[]AttributeValueFQN{uns2spk},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, ""}},
},
{
"spk.uns => spk",
[]AttributeValueFQN{spk2uns},
[]string{kasUs},
[]keySplitStep{{specifiedKas, ""}},
},
{
"spk.spk => value.spk",
[]AttributeValueFQN{spk2spk},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, ""}},
},
{
"spk.spk & spk.uns => value.spk || attr.spk",
[]AttributeValueFQN{spk2spk, spk2uns},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, "1"}, {specifiedKas, "1"}},
},
{
"spk.uns & spk.spk => value.spk || attr.spk",
[]AttributeValueFQN{spk2spk, spk2uns},
[]string{kasUs},
[]keySplitStep{{specifiedKas, "1"}, {evenMoreSpecificKas, "1"}},
},
{
"uns.spk & uns.uns => spk",
[]AttributeValueFQN{uns2spk, uns2uns},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, ""}},
},
{
"uns.uns & uns.spk => spk",
[]AttributeValueFQN{uns2spk, uns2uns},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, ""}},
},
{
"uns.uns & spk.spk => spk",
[]AttributeValueFQN{uns2spk, uns2uns},
[]string{kasUs},
[]keySplitStep{{evenMoreSpecificKas, ""}},
},
} {
t.Run(tc.n, func(t *testing.T) {
reasoner, err := newGranterFromService(context.Background(), newKasKeyCache(), &mockAttributesClient{}, tc.policy...)
require.NoError(t, err)
i := 0
plan, err := reasoner.plan(tc.defaults, func() string {
i++
return fmt.Sprintf("%d", i)
})
require.NoError(t, err)
assert.ElementsMatch(t, tc.plan, plan)
})
}
}