From 17196fbf6bb80b0ba4327bec3f1f2f8e4633ec6d Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Fri, 13 Mar 2026 08:59:03 -0500 Subject: [PATCH 1/2] add the ability to create subject mapping and condition set, to unblock xtest. --- cmd/policy/subjectConditionSets.go | 11 +++++++++- cmd/policy/subjectMappings.go | 11 +++++++++- .../policy/subject-condition-sets/create.md | 6 +++++- docs/man/policy/subject-mappings/create.md | 7 +++++-- go.mod | 2 +- go.sum | 2 ++ pkg/handlers/subjectConditionSets.go | 13 +++++++++--- pkg/handlers/subjectmappings.go | 21 ++++++++++++++++--- 8 files changed, 61 insertions(+), 12 deletions(-) diff --git a/cmd/policy/subjectConditionSets.go b/cmd/policy/subjectConditionSets.go index bd0ed420..b9c25983 100644 --- a/cmd/policy/subjectConditionSets.go +++ b/cmd/policy/subjectConditionSets.go @@ -55,6 +55,7 @@ func createSubjectConditionSet(cmd *cobra.Command, args []string) { defer h.Close() var ssBytes []byte + namespace := c.Flags.GetRequiredString("namespace") ssFlagJSON := c.Flags.GetOptionalString("subject-sets") ssFileJSON := c.Flags.GetOptionalString("subject-sets-file-json") metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) @@ -88,7 +89,7 @@ func createSubjectConditionSet(cmd *cobra.Command, args []string) { cli.ExitWithError("Error unmarshalling subject sets", err) } - scs, err := h.CreateSubjectConditionSet(cmd.Context(), ss, getMetadataMutable(metadataLabels)) + scs, err := h.CreateSubjectConditionSet(cmd.Context(), namespace, ss, getMetadataMutable(metadataLabels)) if err != nil { cli.ExitWithError("Error creating subject condition set", err) } @@ -102,6 +103,9 @@ func createSubjectConditionSet(cmd *cobra.Command, args []string) { {"Id", scs.GetId()}, {"SubjectSets", string(subjectSetsJSON)}, } + if scs.GetNamespace() != nil { + rows = append(rows, []string{"Namespace: FQN", scs.GetNamespace().GetFqn()}) + } if mdRows := getMetadataRows(scs.GetMetadata()); mdRows != nil { rows = append(rows, mdRows...) @@ -321,6 +325,11 @@ func initSubjectConditionSetsCommands() { man.WithRun(createSubjectConditionSet), ) injectLabelFlags(&createDoc.Command, false) + createDoc.Flags().String( + createDoc.GetDocFlag("namespace").Name, + createDoc.GetDocFlag("namespace").Default, + createDoc.GetDocFlag("namespace").Description, + ) createDoc.Flags().StringP( createDoc.GetDocFlag("subject-sets").Name, createDoc.GetDocFlag("subject-sets").Shorthand, diff --git a/cmd/policy/subjectMappings.go b/cmd/policy/subjectMappings.go index 12acdd55..9d513bcb 100644 --- a/cmd/policy/subjectMappings.go +++ b/cmd/policy/subjectMappings.go @@ -109,6 +109,7 @@ func policyCreateSubjectMapping(cmd *cobra.Command, args []string) { h := common.NewHandler(c) defer h.Close() + namespace := c.Flags.GetRequiredString("namespace") attrValueID := c.Flags.GetRequiredID("attribute-value-id") actionFlagValues = c.Flags.GetStringSlice("action", actionFlagValues, cli.FlagsStringSliceOptions{Min: 0}) metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) @@ -147,7 +148,7 @@ func policyCreateSubjectMapping(cmd *cobra.Command, args []string) { } } - mapping, err := h.CreateNewSubjectMapping(cmd.Context(), attrValueID, actions, existingSCSId, scs, getMetadataMutable(metadataLabels)) + mapping, err := h.CreateNewSubjectMapping(cmd.Context(), namespace, attrValueID, actions, existingSCSId, scs, getMetadataMutable(metadataLabels)) if err != nil { cli.ExitWithError("Failed to create subject mapping", err) } @@ -171,6 +172,9 @@ func policyCreateSubjectMapping(cmd *cobra.Command, args []string) { {"Subject Condition Set: Id", mapping.GetSubjectConditionSet().GetId()}, {"Subject Condition Set", string(subjectSetsJSON)}, } + if mapping.GetNamespace() != nil { + rows = append(rows, []string{"Namespace: FQN", mapping.GetNamespace().GetFqn()}) + } if mdRows := getMetadataRows(mapping.GetMetadata()); mdRows != nil { rows = append(rows, mdRows...) @@ -336,6 +340,11 @@ func initSubjectMappingsCommands() { createDoc := man.Docs.GetCommand("policy/subject-mappings/create", man.WithRun(policyCreateSubjectMapping), ) + createDoc.Flags().String( + createDoc.GetDocFlag("namespace").Name, + createDoc.GetDocFlag("namespace").Default, + createDoc.GetDocFlag("namespace").Description, + ) createDoc.Flags().StringP( createDoc.GetDocFlag("attribute-value-id").Name, createDoc.GetDocFlag("attribute-value-id").Shorthand, diff --git a/docs/man/policy/subject-condition-sets/create.md b/docs/man/policy/subject-condition-sets/create.md index 7ace9291..6f65aef5 100644 --- a/docs/man/policy/subject-condition-sets/create.md +++ b/docs/man/policy/subject-condition-sets/create.md @@ -8,6 +8,10 @@ command: - add - new flags: + - name: namespace + description: Namespace ID or FQN for the subject condition set + required: true + default: '' - name: subject-sets description: A JSON array of subject sets, containing a list of condition groups, each with one or more conditions shorthand: s @@ -100,7 +104,7 @@ For more information about subject condition sets, see the `subject-condition-se The following subject condition set would resolve to true if the field at `.example.field.one` is `myvalue` or `myothervalue1`, or the field at `.example.field.two` is not equal to `notpresentvalue`. ```shell -otdfctl policy subject-condition-set create --subject-sets '[ +otdfctl policy subject-condition-set create --namespace https://example.com --subject-sets '[ { "condition_groups": [ { diff --git a/docs/man/policy/subject-mappings/create.md b/docs/man/policy/subject-mappings/create.md index f6193a89..0c3eeac5 100644 --- a/docs/man/policy/subject-mappings/create.md +++ b/docs/man/policy/subject-mappings/create.md @@ -7,6 +7,9 @@ command: - add - c flags: + - name: namespace + description: Namespace ID or FQN for the subject mapping and any newly created subject condition set + required: true - name: attribute-value-id description: The ID of the attribute value to map to a subject condition set shorthand: a @@ -45,12 +48,12 @@ For more information about subject condition sets, see the `subject-condition-se Create a subject mapping for a 'read' action linking to an existing subject condition set: ```shell -otdfctl policy subject-mapping create --attribute-value-id 891cfe85-b381-4f85-9699-5f7dbfe2a9ab --action read --subject-condition-set-id 8dc98f65-5f0a-4444-bfd1-6a818dc7b447 +otdfctl policy subject-mapping create --namespace 7650f02a-be00-4faa-a1d1-37cded5e23dc --attribute-value-id 891cfe85-b381-4f85-9699-5f7dbfe2a9ab --action read --subject-condition-set-id 8dc98f65-5f0a-4444-bfd1-6a818dc7b447 ``` Or you can create a mapping for 'read' or 'create' linking to a new subject condition set: ```shell -otdfctl policy subject-mapping create --attribute-value-id 891cfe85-b381-4f85-9699-5f7dbfe2a9ab --action create --action update --subject-condition-set-new '[ +otdfctl policy subject-mapping create --namespace https://example.com --attribute-value-id 891cfe85-b381-4f85-9699-5f7dbfe2a9ab --action create --action update --subject-condition-set-new '[ { "condition_groups": [ { diff --git a/go.mod b/go.mod index 7af4fe4b..62b8c220 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/jrschumacher/go-osprofiles v0.0.0-20251201220924-3d077c5481e5 github.com/opentdf/platform/lib/flattening v0.1.3 github.com/opentdf/platform/lib/ocrypto v0.10.0 - github.com/opentdf/platform/protocol/go v0.17.0 + github.com/opentdf/platform/protocol/go v0.17.1-0.20260312154851-708936d345e4 github.com/opentdf/platform/sdk v0.13.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index 60e43d84..1c1051fe 100644 --- a/go.sum +++ b/go.sum @@ -244,6 +244,8 @@ github.com/opentdf/platform/lib/ocrypto v0.10.0 h1:7dn/z/1qH3p+gWCrfOoU7hj9XF/p5 github.com/opentdf/platform/lib/ocrypto v0.10.0/go.mod h1:WASkoHreqgTFImB/gJW42VTdpi9AkgkmaW19y/fU+Ew= github.com/opentdf/platform/protocol/go v0.17.0 h1:pZUnX/jp741SW/gYwa5eAj8P7yqHQV58cwPpxEDR/Ik= github.com/opentdf/platform/protocol/go v0.17.0/go.mod h1:4lsBu86yrOWdhqIko8/x5ndamOtM8iDNZYBguF9ZiQQ= +github.com/opentdf/platform/protocol/go v0.17.1-0.20260312154851-708936d345e4 h1:N7n00LLShaBLiZz+veT767aSHTNjKXTzvuFo3skO4Dk= +github.com/opentdf/platform/protocol/go v0.17.1-0.20260312154851-708936d345e4/go.mod h1:jeDa0o2jce1yELNxZ92hscz0mxxqq7GtqC9EoYCpNW0= github.com/opentdf/platform/sdk v0.13.0 h1:jhhCLE1Y57Y20g6TUEg/zqCfid8m4KPfbaaBlg6oAsM= github.com/opentdf/platform/sdk v0.13.0/go.mod h1:x80F65+dGzxDTq8iqVIbCrbDDB9oYCYGjh4duoH6biM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= diff --git a/pkg/handlers/subjectConditionSets.go b/pkg/handlers/subjectConditionSets.go index 42b88972..b1e2e5db 100644 --- a/pkg/handlers/subjectConditionSets.go +++ b/pkg/handlers/subjectConditionSets.go @@ -29,13 +29,20 @@ func (h Handler) ListSubjectConditionSets(ctx context.Context, limit, offset int } // Creates and returns the created subject condition set -func (h Handler) CreateSubjectConditionSet(ctx context.Context, ss []*policy.SubjectSet, metadata *common.MetadataMutable) (*policy.SubjectConditionSet, error) { - resp, err := h.sdk.SubjectMapping.CreateSubjectConditionSet(ctx, &subjectmapping.CreateSubjectConditionSetRequest{ +func (h Handler) CreateSubjectConditionSet(ctx context.Context, namespace string, ss []*policy.SubjectSet, metadata *common.MetadataMutable) (*policy.SubjectConditionSet, error) { + namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) + req := &subjectmapping.CreateSubjectConditionSetRequest{ SubjectConditionSet: &subjectmapping.SubjectConditionSetCreate{ SubjectSets: ss, Metadata: metadata, }, - }) + } + if namespaceID != "" { + req.NamespaceId = namespaceID + } else { + req.NamespaceFqn = namespaceFQN + } + resp, err := h.sdk.SubjectMapping.CreateSubjectConditionSet(ctx, req) if err != nil { return nil, err } diff --git a/pkg/handlers/subjectmappings.go b/pkg/handlers/subjectmappings.go index d234e68f..0d287d98 100644 --- a/pkg/handlers/subjectmappings.go +++ b/pkg/handlers/subjectmappings.go @@ -3,6 +3,7 @@ package handlers import ( "context" + "github.com/google/uuid" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/protocol/go/policy/subjectmapping" @@ -15,6 +16,13 @@ const ( SubjectMappingOperatorUnspecified = "UNSPECIFIED" ) +func parseNamespaceIDOrFQN(namespace string) (string, string) { + if _, err := uuid.Parse(namespace); err != nil { + return "", namespace + } + return namespace, "" +} + var SubjectMappingOperatorEnumChoices = []string{SubjectMappingOperatorIn, SubjectMappingOperatorNotIn, SubjectMappingOperatorUnspecified} func (h Handler) GetSubjectMapping(ctx context.Context, id string) (*policy.SubjectMapping, error) { @@ -34,14 +42,21 @@ func (h Handler) ListSubjectMappings(ctx context.Context, limit, offset int32) ( } // Creates and returns the created subject mapping -func (h Handler) CreateNewSubjectMapping(ctx context.Context, attrValID string, actions []*policy.Action, existingSCSId string, newScs *subjectmapping.SubjectConditionSetCreate, m *common.MetadataMutable) (*policy.SubjectMapping, error) { - resp, err := h.sdk.SubjectMapping.CreateSubjectMapping(ctx, &subjectmapping.CreateSubjectMappingRequest{ +func (h Handler) CreateNewSubjectMapping(ctx context.Context, namespace string, attrValID string, actions []*policy.Action, existingSCSId string, newScs *subjectmapping.SubjectConditionSetCreate, m *common.MetadataMutable) (*policy.SubjectMapping, error) { + namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) + req := &subjectmapping.CreateSubjectMappingRequest{ AttributeValueId: attrValID, Actions: actions, ExistingSubjectConditionSetId: existingSCSId, NewSubjectConditionSet: newScs, Metadata: m, - }) + } + if namespaceID != "" { + req.NamespaceId = namespaceID + } else { + req.NamespaceFqn = namespaceFQN + } + resp, err := h.sdk.SubjectMapping.CreateSubjectMapping(ctx, req) if err != nil { return nil, err } From dd76753e7c9794fa4423963bf426adcc7d4d3492 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Fri, 13 Mar 2026 11:05:13 -0500 Subject: [PATCH 2/2] allow subject mapping and condition sets to be optional for xtest. --- cmd/policy/subjectConditionSets.go | 2 +- cmd/policy/subjectMappings.go | 2 +- docs/man/policy/subject-condition-sets/create.md | 4 ++-- docs/man/policy/subject-mappings/create.md | 4 ++-- pkg/handlers/subjectConditionSets.go | 12 +++++++----- pkg/handlers/subjectmappings.go | 12 +++++++----- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/cmd/policy/subjectConditionSets.go b/cmd/policy/subjectConditionSets.go index b9c25983..950924fb 100644 --- a/cmd/policy/subjectConditionSets.go +++ b/cmd/policy/subjectConditionSets.go @@ -55,7 +55,7 @@ func createSubjectConditionSet(cmd *cobra.Command, args []string) { defer h.Close() var ssBytes []byte - namespace := c.Flags.GetRequiredString("namespace") + namespace := c.Flags.GetOptionalString("namespace") ssFlagJSON := c.Flags.GetOptionalString("subject-sets") ssFileJSON := c.Flags.GetOptionalString("subject-sets-file-json") metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) diff --git a/cmd/policy/subjectMappings.go b/cmd/policy/subjectMappings.go index 9d513bcb..564e9d1c 100644 --- a/cmd/policy/subjectMappings.go +++ b/cmd/policy/subjectMappings.go @@ -109,7 +109,7 @@ func policyCreateSubjectMapping(cmd *cobra.Command, args []string) { h := common.NewHandler(c) defer h.Close() - namespace := c.Flags.GetRequiredString("namespace") + namespace := c.Flags.GetOptionalString("namespace") attrValueID := c.Flags.GetRequiredID("attribute-value-id") actionFlagValues = c.Flags.GetStringSlice("action", actionFlagValues, cli.FlagsStringSliceOptions{Min: 0}) metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) diff --git a/docs/man/policy/subject-condition-sets/create.md b/docs/man/policy/subject-condition-sets/create.md index 6f65aef5..eaa2a659 100644 --- a/docs/man/policy/subject-condition-sets/create.md +++ b/docs/man/policy/subject-condition-sets/create.md @@ -9,8 +9,8 @@ command: - new flags: - name: namespace - description: Namespace ID or FQN for the subject condition set - required: true + description: Optional namespace ID or FQN for the subject condition set + required: false default: '' - name: subject-sets description: A JSON array of subject sets, containing a list of condition groups, each with one or more conditions diff --git a/docs/man/policy/subject-mappings/create.md b/docs/man/policy/subject-mappings/create.md index 0c3eeac5..ce57e100 100644 --- a/docs/man/policy/subject-mappings/create.md +++ b/docs/man/policy/subject-mappings/create.md @@ -8,8 +8,8 @@ command: - c flags: - name: namespace - description: Namespace ID or FQN for the subject mapping and any newly created subject condition set - required: true + description: Optional namespace ID or FQN for the subject mapping and any newly created subject condition set + required: false - name: attribute-value-id description: The ID of the attribute value to map to a subject condition set shorthand: a diff --git a/pkg/handlers/subjectConditionSets.go b/pkg/handlers/subjectConditionSets.go index b1e2e5db..fa4fccd1 100644 --- a/pkg/handlers/subjectConditionSets.go +++ b/pkg/handlers/subjectConditionSets.go @@ -30,17 +30,19 @@ func (h Handler) ListSubjectConditionSets(ctx context.Context, limit, offset int // Creates and returns the created subject condition set func (h Handler) CreateSubjectConditionSet(ctx context.Context, namespace string, ss []*policy.SubjectSet, metadata *common.MetadataMutable) (*policy.SubjectConditionSet, error) { - namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) req := &subjectmapping.CreateSubjectConditionSetRequest{ SubjectConditionSet: &subjectmapping.SubjectConditionSetCreate{ SubjectSets: ss, Metadata: metadata, }, } - if namespaceID != "" { - req.NamespaceId = namespaceID - } else { - req.NamespaceFqn = namespaceFQN + if namespace != "" { + namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) + if namespaceID != "" { + req.NamespaceId = namespaceID + } else { + req.NamespaceFqn = namespaceFQN + } } resp, err := h.sdk.SubjectMapping.CreateSubjectConditionSet(ctx, req) if err != nil { diff --git a/pkg/handlers/subjectmappings.go b/pkg/handlers/subjectmappings.go index 0d287d98..c4b04261 100644 --- a/pkg/handlers/subjectmappings.go +++ b/pkg/handlers/subjectmappings.go @@ -43,7 +43,6 @@ func (h Handler) ListSubjectMappings(ctx context.Context, limit, offset int32) ( // Creates and returns the created subject mapping func (h Handler) CreateNewSubjectMapping(ctx context.Context, namespace string, attrValID string, actions []*policy.Action, existingSCSId string, newScs *subjectmapping.SubjectConditionSetCreate, m *common.MetadataMutable) (*policy.SubjectMapping, error) { - namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) req := &subjectmapping.CreateSubjectMappingRequest{ AttributeValueId: attrValID, Actions: actions, @@ -51,10 +50,13 @@ func (h Handler) CreateNewSubjectMapping(ctx context.Context, namespace string, NewSubjectConditionSet: newScs, Metadata: m, } - if namespaceID != "" { - req.NamespaceId = namespaceID - } else { - req.NamespaceFqn = namespaceFQN + if namespace != "" { + namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace) + if namespaceID != "" { + req.NamespaceId = namespaceID + } else { + req.NamespaceFqn = namespaceFQN + } } resp, err := h.sdk.SubjectMapping.CreateSubjectMapping(ctx, req) if err != nil {