Skip to content
Open
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
11 changes: 10 additions & 1 deletion cmd/policy/subjectConditionSets.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func createSubjectConditionSet(cmd *cobra.Command, args []string) {
defer h.Close()
var ssBytes []byte

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})
Expand Down Expand Up @@ -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)
}
Expand All @@ -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...)
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 10 additions & 1 deletion cmd/policy/subjectMappings.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func policyCreateSubjectMapping(cmd *cobra.Command, args []string) {
h := common.NewHandler(c)
defer h.Close()

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})
Expand Down Expand Up @@ -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)
}
Expand All @@ -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...)
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion docs/man/policy/subject-condition-sets/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ command:
- add
- new
flags:
- name: namespace
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
shorthand: s
Expand Down Expand Up @@ -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": [
{
Expand Down
7 changes: 5 additions & 2 deletions docs/man/policy/subject-mappings/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ command:
- add
- c
flags:
- name: namespace
description: Optional namespace ID or FQN for the subject mapping and any newly created subject condition set
required: false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other flag definitions (like in docs/man/policy/subject-condition-sets/create.md), it's best to explicitly add default: '' for the optional namespace flag. While the behavior might be correct without it due to zero-value defaults, explicitly defining it improves the clarity and robustness of the command definition parsing.

Suggested change
required: false
required: false
default: ''

- name: attribute-value-id
description: The ID of the attribute value to map to a subject condition set
shorthand: a
Expand Down Expand Up @@ -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": [
{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
15 changes: 12 additions & 3 deletions pkg/handlers/subjectConditionSets.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,22 @@ 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) {
req := &subjectmapping.CreateSubjectConditionSetRequest{
SubjectConditionSet: &subjectmapping.SubjectConditionSetCreate{
SubjectSets: ss,
Metadata: metadata,
},
})
}
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 {
return nil, err
}
Expand Down
23 changes: 20 additions & 3 deletions pkg/handlers/subjectmappings.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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) {
Expand All @@ -34,14 +42,23 @@ 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) {
req := &subjectmapping.CreateSubjectMappingRequest{
AttributeValueId: attrValID,
Actions: actions,
ExistingSubjectConditionSetId: existingSCSId,
NewSubjectConditionSet: newScs,
Metadata: m,
})
}
if namespace != "" {
namespaceID, namespaceFQN := parseNamespaceIDOrFQN(namespace)
if namespaceID != "" {
req.NamespaceId = namespaceID
} else {
req.NamespaceFqn = namespaceFQN
}
}
Comment on lines +53 to +60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for setting the namespace on the request is duplicated in pkg/handlers/subjectConditionSets.go. To improve maintainability and avoid future bugs, consider extracting this logic into a private helper function within the handlers package. This function could be used in both CreateNewSubjectMapping and CreateSubjectConditionSet to centralize the namespace handling.

resp, err := h.sdk.SubjectMapping.CreateSubjectMapping(ctx, req)
if err != nil {
return nil, err
}
Expand Down
Loading