diff --git a/.github/spellcheck.ignore b/.github/spellcheck.ignore index d331deee..2e9949ad 100644 --- a/.github/spellcheck.ignore +++ b/.github/spellcheck.ignore @@ -46,8 +46,10 @@ TODO TUI URI Unassign +ZTDF abc acmeco +appliesToState args attr auth @@ -55,6 +57,7 @@ cli clientId clientSecret config +cryptographically data-centric decrypt decryptable @@ -79,8 +82,10 @@ localhost namespace namespaces nano +nato no-verify-assertions ns +ocl otdfctl performant poc @@ -91,6 +96,7 @@ resolvers scs sel sm +stanag stdin stdout stdout @@ -98,6 +104,7 @@ subcommand subcs subm submap +tdo tdf tdf-type tls @@ -105,16 +112,10 @@ tls-no-verify txt unassign unassignment +un-mapped upsert uri with-client-creds with-client-creds-file yaml -ZTDF -ztdf -tdo -appliesToState -stanag -nato -ocl -cryptographically \ No newline at end of file +ztdf \ No newline at end of file diff --git a/cmd/kas-registry.go b/cmd/kas-registry.go index ecbc71ed..925d57e8 100644 --- a/cmd/kas-registry.go +++ b/cmd/kas-registry.go @@ -216,9 +216,7 @@ func policy_deleteKeyAccessRegistry(cmd *cobra.Command, args []string) { cli.ExitWithError(errMsg, err) } - if !force { - cli.ConfirmAction(cli.ActionDelete, "Registered KAS", id, false) - } + cli.ConfirmAction(cli.ActionDelete, "Registered KAS", id, force) if _, err := h.DeleteKasRegistryEntry(id); err != nil { errMsg := fmt.Sprintf("Failed to delete Registered KAS entry (%s)", id) diff --git a/cmd/policy-attributeNamespaces.go b/cmd/policy-attributeNamespaces.go index 05ebe469..ed3db7df 100644 --- a/cmd/policy-attributeNamespaces.go +++ b/cmd/policy-attributeNamespaces.go @@ -113,9 +113,7 @@ func policy_deactivateAttributeNamespace(cmd *cobra.Command, args []string) { cli.ExitWithError(errMsg, err) } - if !force { - cli.ConfirmAction(cli.ActionDeactivate, "namespace", ns.GetName(), false) - } + cli.ConfirmAction(cli.ActionDeactivate, "namespace", ns.GetName(), force) d, err := h.DeactivateNamespace(id) if err != nil { diff --git a/cmd/policy-attributes.go b/cmd/policy-attributes.go index 5d63ab87..9a855f4f 100644 --- a/cmd/policy-attributes.go +++ b/cmd/policy-attributes.go @@ -140,9 +140,7 @@ func policy_deactivateAttribute(cmd *cobra.Command, args []string) { cli.ExitWithError(errMsg, err) } - if !force { - cli.ConfirmAction(cli.ActionDeactivate, "attribute", attr.GetName(), false) - } + cli.ConfirmAction(cli.ActionDeactivate, "attribute", attr.GetName(), force) attr, err = h.DeactivateAttribute(id) if err != nil { diff --git a/cmd/policy-resourceMappings.go b/cmd/policy-resourceMappings.go index 82c87dbd..52a0ce4b 100644 --- a/cmd/policy-resourceMappings.go +++ b/cmd/policy-resourceMappings.go @@ -139,9 +139,7 @@ func policy_deleteResourceMapping(cmd *cobra.Command, args []string) { id := c.Flags.GetRequiredID("id") force := c.Flags.GetOptionalBool("force") - if !force { - cli.ConfirmAction(cli.ActionDelete, "resource-mapping", id, false) - } + cli.ConfirmAction(cli.ActionDelete, "resource-mapping", id, force) resourceMapping, err := h.GetResourceMapping(id) if err != nil { diff --git a/cmd/policy-subjectConditionSets.go b/cmd/policy-subjectConditionSets.go index dd69e13e..10e5b77e 100644 --- a/cmd/policy-subjectConditionSets.go +++ b/cmd/policy-subjectConditionSets.go @@ -255,9 +255,7 @@ func policy_deleteSubjectConditionSet(cmd *cobra.Command, args []string) { cli.ExitWithError(fmt.Sprintf("Subject Condition Set with id %s not found", id), err) } - if !force { - cli.ConfirmAction(cli.ActionDelete, "Subject Condition Set", id, false) - } + cli.ConfirmAction(cli.ActionDelete, "Subject Condition Sets", "all unmapped", force) if err := h.DeleteSubjectConditionSet(id); err != nil { cli.ExitWithError(fmt.Sprintf("Subject Condition Set with id %s not found", id), err) @@ -281,6 +279,34 @@ func policy_deleteSubjectConditionSet(cmd *cobra.Command, args []string) { HandleSuccess(cmd, scs.GetId(), t, scs) } +func policy_pruneSubjectConditionSet(cmd *cobra.Command, args []string) { + c := cli.New(cmd, args) + h := NewHandler(c) + defer h.Close() + + force := c.Flags.GetOptionalBool("force") + + cli.ConfirmAction(cli.ActionDelete, "all unmapped Subject Condition Sets", "", force) + + pruned, err := h.PruneSubjectConditionSets() + if err != nil { + cli.ExitWithError("Failed to prune unmapped Subject Condition Sets", err) + } + + rows := []table.Row{} + for _, scs := range pruned { + rows = append(rows, table.NewRow(table.RowData{ + "id": scs.GetId(), + })) + } + + t := cli.NewTable( + cli.NewUUIDColumn(), + ) + t = t.WithRows(rows) + HandleSuccess(cmd, "", t, pruned) +} + var policy_subjectConditionSetsCmd *cobra.Command func init() { @@ -354,6 +380,16 @@ func init() { deleteDoc.GetDocFlag("force").Description, ) + pruneDoc := man.Docs.GetCommand( + "policy/subject-condition-sets/prune", + man.WithRun(policy_pruneSubjectConditionSet), + ) + pruneDoc.Flags().Bool( + pruneDoc.GetDocFlag("force").Name, + false, + pruneDoc.GetDocFlag("force").Description, + ) + doc := man.Docs.GetCommand("policy/subject-condition-sets", man.WithSubcommands( createDoc, @@ -361,6 +397,7 @@ func init() { listDoc, updateDoc, deleteDoc, + pruneDoc, ), ) policy_subjectConditionSetsCmd = &doc.Command diff --git a/cmd/policy-subjectMappings.go b/cmd/policy-subjectMappings.go index a5f6d76a..9b98766b 100644 --- a/cmd/policy-subjectMappings.go +++ b/cmd/policy-subjectMappings.go @@ -197,9 +197,7 @@ func policy_deleteSubjectMapping(cmd *cobra.Command, args []string) { cli.ExitWithError(errMsg, err) } - if !force { - cli.ConfirmAction(cli.ActionDelete, "subject mapping", sm.GetId(), false) - } + cli.ConfirmAction(cli.ActionDelete, "subject mapping", sm.GetId(), force) deleted, err := h.DeleteSubjectMapping(id) if err != nil { diff --git a/docs/man/policy/subject-condition-sets/prune.md b/docs/man/policy/subject-condition-sets/prune.md new file mode 100644 index 00000000..e2842050 --- /dev/null +++ b/docs/man/policy/subject-condition-sets/prune.md @@ -0,0 +1,13 @@ +--- +title: Prune (delete all un-mapped Subject Condition Sets) + +command: + name: prune + flags: + - name: force + description: Force prune without interactive confirmation (dangerous) +--- + +This command will delete all Subject Condition Sets that are not utilized within any Subject Mappings and are therefore 'stranded'. + +For more information about subject condition sets, see the `subject-condition-sets` subcommand. diff --git a/e2e/subject-condition-sets.bats b/e2e/subject-condition-sets.bats index 3dc3c0a2..f6ae5e3c 100755 --- a/e2e/subject-condition-sets.bats +++ b/e2e/subject-condition-sets.bats @@ -124,3 +124,23 @@ teardown_file() { assert_success assert_output --partial "$CREATED_ID" } + +@test "Prune SCS - deletes unmapped SCS alone" { + echo -n "$SCS_1" > scs.json + + UNMAPPED_ID=$(./otdfctl policy scs create --subject-sets-file-json scs.json $HOST $WITH_CREDS --json | jq -r '.id') + MAPPED_ID=$(./otdfctl policy scs create --subject-sets "$SCS_2" $HOST $WITH_CREDS --json | jq -r '.id') + + # create a namespace, definition, value, sm to the value with the MAPPED_ID SCS + NS_ID=$(./otdfctl policy attributes namespaces create -n 'scs.net' $HOST $WITH_CREDS --json | jq -r '.id') + ATTR_ID=$(./otdfctl policy attributes create -n 'my_attr' --namespace "$NS_ID" -r "ANY_OF" $HOST $WITH_CREDS --json | jq -r '.id') + VAL_ID=$(./otdfctl policy attributes values create -v 'my_value' -a "$ATTR_ID" $HOST $WITH_CREDS --json | jq -r '.id') + + run ./otdfctl policy sm create -s 'DECRYPT' -a "$VAL_ID" --subject-condition-set-id "$MAPPED_ID" $HOST $WITH_CREDS + assert_success + + run_otdfctl_scs prune --force + assert_success + assert_output --partial "$UNMAPPED_ID" + refute_output --partial "$MAPPED_ID" +} \ No newline at end of file diff --git a/pkg/handlers/subjectConditionSets.go b/pkg/handlers/subjectConditionSets.go index 2f2d8366..ea8dbd8d 100644 --- a/pkg/handlers/subjectConditionSets.go +++ b/pkg/handlers/subjectConditionSets.go @@ -59,3 +59,11 @@ func (h Handler) DeleteSubjectConditionSet(id string) error { }) return err } + +func (h Handler) PruneSubjectConditionSets() ([]*policy.SubjectConditionSet, error) { + rsp, err := h.sdk.SubjectMapping.DeleteAllUnmappedSubjectConditionSets(h.ctx, &subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest{}) + if err != nil { + return nil, err + } + return rsp.GetSubjectConditionSets(), nil +}