Skip to content
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
7 changes: 7 additions & 0 deletions docs/grpc/index.html

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

3 changes: 3 additions & 0 deletions docs/openapi/policy/objects.openapi.yaml

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

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

22 changes: 21 additions & 1 deletion otdfctl/e2e/registered-resources.bats
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,24 @@ teardown_file() {
run_otdfctl_reg_res_values delete --id $created_id_with_action_attr_vals --force
}

@test "Create a registered resource value includes FQN in JSON output" {
value="test_create_rr_val_fqn"
expected_fqn="https://$NS_NAME/reg_res/$RR_NAME/value/$value"

run_otdfctl_reg_res_values create --resource "$RR_ID" --value "$value" --json
assert_success
created_id=$(echo "$output" | jq -r '.id')
assert_equal "$(echo "$output" | jq -r '.value')" "$value"
assert_equal "$(echo "$output" | jq -r '.fqn')" "$expected_fqn"

run_otdfctl_reg_res_values get --id "$created_id" --json
assert_success
assert_equal "$(echo "$output" | jq -r '.id')" "$created_id"
assert_equal "$(echo "$output" | jq -r '.fqn')" "$expected_fqn"

run_otdfctl_reg_res_values delete --id "$created_id" --force
}

@test "Create a registered resource value - Bad" {
# bad resource value names
run_otdfctl_reg_res_values create --resource "$RR_ID" --value ends_underscored_
Expand Down Expand Up @@ -326,16 +344,18 @@ teardown_file() {
assert_success
[ "$(echo "$output" | jq -r '.id')" = "$created_id" ]
[ "$(echo "$output" | jq -r '.value')" = "test_get_rr_val" ]
[ "$(echo "$output" | jq -r '.fqn')" = "https://$NS_NAME/reg_res/$RR_NAME/value/test_get_rr_val" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].action.id')" = "$READ_ACTION_ID" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].action.name')" = "$READ_ACTION_NAME" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].attribute_value.id')" = "$ATTR_VAL_1_ID" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].attribute_value.fqn')" = "$ATTR_VAL_1_FQN" ]

# get by fqn
run_otdfctl_reg_res_values get --fqn "https://reg_res/$RR_NAME/value/test_get_rr_val" --json
run_otdfctl_reg_res_values get --fqn "https://$NS_NAME/reg_res/$RR_NAME/value/test_get_rr_val" --json
assert_success
[ "$(echo "$output" | jq -r '.id')" = "$created_id" ]
[ "$(echo "$output" | jq -r '.value')" = "test_get_rr_val" ]
[ "$(echo "$output" | jq -r '.fqn')" = "https://$NS_NAME/reg_res/$RR_NAME/value/test_get_rr_val" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].action.id')" = "$READ_ACTION_ID" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].action.name')" = "$READ_ACTION_NAME" ]
[ "$(echo "$output" | jq -r '.action_attribute_values[0].attribute_value.id')" = "$ATTR_VAL_1_ID" ]
Expand Down
451 changes: 230 additions & 221 deletions protocol/go/policy/objects.pb.go

Large diffs are not rendered by default.

59 changes: 53 additions & 6 deletions service/integration/registered_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_Succeeds()
created, err := s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, req)
s.Require().NoError(err)
s.NotNil(created)
s.Equal("https://example.com/reg_res/test_create_res_value/value/value", created.GetFqn())
}

func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_NormalizedName_Succeeds() {
Expand Down Expand Up @@ -876,6 +877,7 @@ func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithInvali
// Get

func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValue_Valid_Succeeds() {
// Fixture registered resources are legacy rows without namespace_id, so the FQN case uses the legacy https://reg_res/... format.
existingRes := s.f.GetRegisteredResourceKey("res_with_values")
existingResValue1 := s.f.GetRegisteredResourceValueKey("res_with_values__value1")

Expand Down Expand Up @@ -971,6 +973,7 @@ func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValue_Invalid_Fails
// Get By FQNs

func (s *RegisteredResourcesSuite) TestGetRegisteredResourceValuesByFQNs_Valid_Succeeds() {
// Fixture registered resources are legacy rows without namespace_id, so these FQNs use the legacy https://reg_res/... format.
existingRes := s.f.GetRegisteredResourceKey("res_with_values")
existingResValue1 := s.f.GetRegisteredResourceValueKey("res_with_values__value1")
existingResValue2 := s.f.GetRegisteredResourceValueKey("res_with_values__value2")
Expand Down Expand Up @@ -1001,6 +1004,7 @@ func (s *RegisteredResourcesSuite) TestGetRegisteredResourceValuesByFQNs_Valid_S
}

func (s *RegisteredResourcesSuite) TestGetRegisteredResourceValuesByFQNs_SomeInvalid_Fails() {
// Fixture registered resources are legacy rows without namespace_id, so the valid FQN uses the legacy https://reg_res/... format.
existingRes := s.f.GetRegisteredResourceKey("res_with_values")
existingResValue1 := s.f.GetRegisteredResourceValueKey("res_with_values__value1")
fqns := []string{
Expand Down Expand Up @@ -1175,6 +1179,7 @@ func (s *AttributesSuite) Test_ListRegisteredResourceValues_Offset_Succeeds() {
}

func (s *RegisteredResourcesSuite) Test_ListRegisteredResourceValues_ByResourceID_Succeeds() {
// Fixture registered resources are legacy rows without namespace_id, so listed values should expose legacy https://reg_res/... FQNs.
existingRes := s.f.GetRegisteredResourceKey("res_with_values")
existingResValue1 := s.f.GetRegisteredResourceValueKey("res_with_values__value1")
existingResValue2 := s.f.GetRegisteredResourceValueKey("res_with_values__value2")
Expand All @@ -1188,10 +1193,15 @@ func (s *RegisteredResourcesSuite) Test_ListRegisteredResourceValues_ByResourceI
s.Len(list.GetValues(), 2)

foundCount := 0
expectedFQNs := map[string]string{
existingResValue1.ID: fmt.Sprintf("https://reg_res/%s/value/%s", existingRes.Name, existingResValue1.Value),
existingResValue2.ID: fmt.Sprintf("https://reg_res/%s/value/%s", existingRes.Name, existingResValue2.Value),
}

for _, r := range list.GetValues() {
if r.GetId() == existingResValue1.ID || r.GetId() == existingResValue2.ID {
foundCount++
s.Equal(expectedFQNs[r.GetId()], r.GetFqn())
}
}

Expand Down Expand Up @@ -1246,13 +1256,15 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds()
})
s.Require().NoError(err)
s.NotNil(created)
s.Equal("https://example.com/reg_res/test_update_res_value/value/value", created.GetFqn())

// update with no changes
updated, err := s.db.PolicyClient.UpdateRegisteredResourceValue(s.ctx, &registeredresources.UpdateRegisteredResourceValueRequest{
Id: created.GetId(),
})
s.Require().NoError(err)
s.NotNil(updated)
s.Equal(created.GetFqn(), updated.GetFqn())

// verify resource value not updated
got, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
Expand All @@ -1263,6 +1275,7 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds()
s.Require().NoError(err)
s.Require().NotNil(got)
s.Equal(created.GetValue(), got.GetValue())
s.Equal(created.GetFqn(), got.GetFqn())
s.Equal(labels, got.GetMetadata().GetLabels())
s.Require().Len(got.GetActionAttributeValues(), 1)

Expand Down Expand Up @@ -1295,6 +1308,7 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds()
})
s.Require().NoError(err)
s.NotNil(updated)
s.Equal("https://example.com/reg_res/test_update_res_value/value/updated_value", updated.GetFqn())

// verify resource updated
got, err = s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
Expand All @@ -1305,6 +1319,7 @@ func (s *RegisteredResourcesSuite) Test_UpdateRegisteredResourceValue_Succeeds()
s.Require().NoError(err)
s.NotNil(got)
s.Equal("updated_value", got.GetValue())
s.Equal(updated.GetFqn(), got.GetFqn())
s.Equal(expectedLabels, got.GetMetadata().GetLabels())
metadata := got.GetMetadata()
createdAt := metadata.GetCreatedAt()
Expand Down Expand Up @@ -1428,6 +1443,7 @@ func (s *RegisteredResourcesSuite) Test_DeleteRegisteredResourceValue_Succeeds()
deleted, err := s.db.PolicyClient.DeleteRegisteredResourceValue(s.ctx, created.GetId())
s.Require().NoError(err)
s.Require().Equal(created.GetId(), deleted.GetId())
s.Equal(created.GetFqn(), deleted.GetFqn())

// verify resource value deleted

Expand Down Expand Up @@ -1727,17 +1743,26 @@ func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValue_NamespacedFQN

// Get by namespaced FQN
fqn := fmt.Sprintf("https://example.com/reg_res/%s/value/%s", name, valueName)
got, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
gotByFQN, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
Identifier: &registeredresources.GetRegisteredResourceValueRequest_Fqn{
Fqn: fqn,
},
})
s.Require().NoError(err)
s.NotNil(got)
s.Equal(valueName, got.GetValue())
s.NotNil(got.GetResource())
s.NotNil(got.GetResource().GetNamespace())
s.Equal(nsID, got.GetResource().GetNamespace().GetId())
s.NotNil(gotByFQN)
s.Equal(valueName, gotByFQN.GetValue())
s.Equal(fqn, gotByFQN.GetFqn())
s.NotNil(gotByFQN.GetResource())
s.NotNil(gotByFQN.GetResource().GetNamespace())
s.Equal(nsID, gotByFQN.GetResource().GetNamespace().GetId())

gotByID, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
Identifier: &registeredresources.GetRegisteredResourceValueRequest_Id{
Id: gotByFQN.GetId(),
},
})
s.Require().NoError(err)
s.Equal(gotByFQN.GetFqn(), gotByID.GetFqn())
}

func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValuesByFQNs_NamespacedFormat_Succeeds() {
Expand Down Expand Up @@ -1766,6 +1791,8 @@ func (s *RegisteredResourcesSuite) Test_GetRegisteredResourceValuesByFQNs_Namesp
s.NotNil(fqnMap[fqn2])
s.Equal(val1, fqnMap[fqn1].GetValue())
s.Equal(val2, fqnMap[fqn2].GetValue())
s.Equal(fqn1, fqnMap[fqn1].GetFqn())
s.Equal(fqn2, fqnMap[fqn2].GetFqn())
}

func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_Succeeds() {
Expand All @@ -1786,6 +1813,8 @@ func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_
s.Equal(nsID, res.GetNamespace().GetId())
s.Equal("example.com", res.GetNamespace().GetName())
s.Equal(nsFQN, res.GetNamespace().GetFqn())
s.Require().Len(res.GetValues(), 1)
s.Equal("https://example.com/reg_res/test_ns_in_responses/value/resp-val", res.GetValues()[0].GetFqn())

// Verify namespace in Get response
got, err := s.db.PolicyClient.GetRegisteredResource(s.ctx, &registeredresources.GetRegisteredResourceRequest{
Expand All @@ -1796,6 +1825,8 @@ func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_
s.Require().NoError(err)
s.NotNil(got.GetNamespace())
s.Equal(nsID, got.GetNamespace().GetId())
s.Require().Len(got.GetValues(), 1)
s.Equal(res.GetValues()[0].GetFqn(), got.GetValues()[0].GetFqn())

// Verify namespace in List response
list, err := s.db.PolicyClient.ListRegisteredResources(s.ctx, &registeredresources.ListRegisteredResourcesRequest{
Expand All @@ -1808,6 +1839,8 @@ func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_
found = true
s.NotNil(r.GetNamespace())
s.Equal(nsID, r.GetNamespace().GetId())
s.Require().Len(r.GetValues(), 1)
s.Equal(res.GetValues()[0].GetFqn(), r.GetValues()[0].GetFqn())
}
}
s.True(found)
Expand All @@ -1822,11 +1855,14 @@ func (s *RegisteredResourcesSuite) Test_RegisteredResource_NamespaceInResponses_
s.NotNil(valResp.GetResource())
s.NotNil(valResp.GetResource().GetNamespace())
s.Equal(nsID, valResp.GetResource().GetNamespace().GetId())
s.Equal("https://example.com/reg_res/test_ns_in_responses/value/resp-val", valResp.GetFqn())
}

func (s *RegisteredResourcesSuite) Test_LegacyRegisteredResources_NoNamespace_StillAccessible() {
// Fixture resources are legacy (no namespace) - verify they're still accessible
existingRes := s.f.GetRegisteredResourceKey("res_only")
existingResWithValues := s.f.GetRegisteredResourceKey("res_with_values")
existingResValue := s.f.GetRegisteredResourceValueKey("res_with_values__value1")

got, err := s.db.PolicyClient.GetRegisteredResource(s.ctx, &registeredresources.GetRegisteredResourceRequest{
Identifier: &registeredresources.GetRegisteredResourceRequest_Id{
Expand All @@ -1838,6 +1874,17 @@ func (s *RegisteredResourcesSuite) Test_LegacyRegisteredResources_NoNamespace_St
s.Equal(existingRes.Name, got.GetName())
// Legacy resources have nil namespace
s.Nil(got.GetNamespace())

gotValue, err := s.db.PolicyClient.GetRegisteredResourceValue(s.ctx, &registeredresources.GetRegisteredResourceValueRequest{
Identifier: &registeredresources.GetRegisteredResourceValueRequest_Id{
Id: existingResValue.ID,
},
})
s.Require().NoError(err)
s.NotNil(gotValue)
s.Nil(gotValue.GetResource().GetNamespace())
// Legacy values preserve the pre-namespace FQN shape.
s.Equal(fmt.Sprintf("https://reg_res/%s/value/%s", existingResWithValues.Name, existingResValue.Value), gotValue.GetFqn())
}

func (s *RegisteredResourcesSuite) Test_SameNamespaceEnforcement_DifferentNamespace_Fails() {
Expand Down
39 changes: 36 additions & 3 deletions service/policy/db/registered_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ func hydrateNamespaceFromInterface(nsRaw interface{}) (*policy.Namespace, error)
return ns, nil
}

func registeredResourceValueFQN(namespace *policy.Namespace, resourceName, value string) string {
if namespace == nil {
// Legacy registered resources do not have a namespace; identifier.FQN preserves that as https://reg_res/<name>/value/<value>.
return (&identifier.FullyQualifiedRegisteredResourceValue{
Name: resourceName,
Value: value,
}).FQN()
}

return (&identifier.FullyQualifiedRegisteredResourceValue{
Namespace: namespace.GetName(),
Name: resourceName,
Value: value,
}).FQN()
}

func hydrateRegisteredResourceValueFQNs(values []*policy.RegisteredResourceValue, namespace *policy.Namespace, resourceName string) {
for _, value := range values {
value.Fqn = registeredResourceValueFQN(namespace, resourceName, value.GetValue())
}
}

///
/// Registered Resources
///
Expand Down Expand Up @@ -181,6 +203,7 @@ func (c PolicyDBClient) GetRegisteredResource(ctx context.Context, r *registered
if err = unmarshalRegisteredResourceValuesProto(rr.Values, &values); err != nil {
return nil, err
}
hydrateRegisteredResourceValueFQNs(values, namespace, rr.Name)

return &policy.RegisteredResource{
Id: rr.ID,
Expand Down Expand Up @@ -237,6 +260,7 @@ func (c PolicyDBClient) ListRegisteredResources(ctx context.Context, r *register
if err = unmarshalRegisteredResourceValuesProto(r.Values, &values); err != nil {
return nil, err
}
hydrateRegisteredResourceValueFQNs(values, namespace, r.Name)

rrList[i] = &policy.RegisteredResource{
Id: r.ID,
Expand Down Expand Up @@ -393,6 +417,7 @@ func (c PolicyDBClient) GetRegisteredResourceValue(ctx context.Context, r *regis
return &policy.RegisteredResourceValue{
Id: rv.ID,
Value: rv.Value,
Fqn: registeredResourceValueFQN(namespace, rv.ResourceName, rv.Value),
Metadata: metadata,
Resource: &policy.RegisteredResource{
Id: rv.RegisteredResourceID,
Expand Down Expand Up @@ -475,6 +500,7 @@ func (c PolicyDBClient) ListRegisteredResourceValues(ctx context.Context, r *reg
rvList[i] = &policy.RegisteredResourceValue{
Id: r.ID,
Value: r.Value,
Fqn: registeredResourceValueFQN(namespace, r.ResourceName, r.Value),
Metadata: metadata,
Resource: &policy.RegisteredResource{
Id: r.RegisteredResourceID,
Expand Down Expand Up @@ -554,6 +580,15 @@ func (c PolicyDBClient) UpdateRegisteredResourceValue(ctx context.Context, r *re
}

func (c PolicyDBClient) DeleteRegisteredResourceValue(ctx context.Context, id string) (*policy.RegisteredResourceValue, error) {
deleted, err := c.GetRegisteredResourceValue(ctx, &registeredresources.GetRegisteredResourceValueRequest{
Identifier: &registeredresources.GetRegisteredResourceValueRequest_Id{
Id: id,
},
})
if err != nil {
return nil, err
}
Comment thread
jakedoublev marked this conversation as resolved.

count, err := c.queries.deleteRegisteredResourceValue(ctx, id)
if err != nil {
return nil, db.WrapIfKnownInvalidQueryErr(err)
Expand All @@ -562,9 +597,7 @@ func (c PolicyDBClient) DeleteRegisteredResourceValue(ctx context.Context, id st
return nil, db.ErrNotFound
}

return &policy.RegisteredResourceValue{
Id: id,
}, nil
return deleted, nil
}

///
Expand Down
2 changes: 2 additions & 0 deletions service/policy/objects.proto
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ message RegisteredResourceValue {

repeated ActionAttributeValue action_attribute_values = 4;

string fqn = 5;

// Common metadata
common.Metadata metadata = 100;
}
Expand Down
Loading
Loading