diff --git a/cmd/policy-subjectMappings.go b/cmd/policy-subjectMappings.go index 8230a89c..811f86a0 100644 --- a/cmd/policy-subjectMappings.go +++ b/cmd/policy-subjectMappings.go @@ -3,9 +3,9 @@ package cmd import ( "encoding/json" "fmt" - "strings" "github.com/evertras/bubble-table/table" + "github.com/google/uuid" "github.com/opentdf/otdfctl/pkg/cli" "github.com/opentdf/otdfctl/pkg/handlers" "github.com/opentdf/otdfctl/pkg/man" @@ -14,21 +14,16 @@ import ( "github.com/spf13/cobra" ) -var ( - actionStandardDecrypt = "DECRYPT" - actionStandardTransmit = "TRANSMIT" - actionsStandard []string - actionsCustom []string -) +var actionFlagValues []string -func policy_getSubjectMapping(cmd *cobra.Command, args []string) { +func policyGetSubjectMapping(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() id := c.Flags.GetRequiredID("id") - mapping, err := h.GetSubjectMapping(id) + mapping, err := h.GetSubjectMapping(cmd.Context(), id) if err != nil { errMsg := fmt.Sprintf("Failed to find subject mapping (%s)", id) cli.ExitWithError(errMsg, err) @@ -59,7 +54,7 @@ func policy_getSubjectMapping(cmd *cobra.Command, args []string) { HandleSuccess(cmd, mapping.GetId(), t, mapping) } -func policy_listSubjectMappings(cmd *cobra.Command, args []string) { +func policyListSubjectMappings(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() @@ -67,7 +62,7 @@ func policy_listSubjectMappings(cmd *cobra.Command, args []string) { limit := c.Flags.GetRequiredInt32("limit") offset := c.Flags.GetRequiredInt32("offset") - list, page, err := h.ListSubjectMappings(limit, offset) + list, page, err := h.ListSubjectMappings(cmd.Context(), limit, offset) if err != nil { cli.ExitWithError("Failed to get subject mappings", err) } @@ -105,36 +100,37 @@ func policy_listSubjectMappings(cmd *cobra.Command, args []string) { HandleSuccess(cmd, "", t, list) } -func policy_createSubjectMapping(cmd *cobra.Command, args []string) { +func policyCreateSubjectMapping(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() attrValueId := c.Flags.GetRequiredID("attribute-value-id") - actionsStandard = c.Flags.GetStringSlice("action-standard", actionsStandard, cli.FlagsStringSliceOptions{Min: 0}) - actionsCustom = c.Flags.GetStringSlice("action-custom", actionsCustom, cli.FlagsStringSliceOptions{Min: 0}) + actionFlagValues = c.Flags.GetStringSlice("action", actionFlagValues, cli.FlagsStringSliceOptions{Min: 0}) metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) existingSCSId := c.Flags.GetOptionalID("subject-condition-set-id") // NOTE: labels within a new Subject Condition Set created on a SM creation are not supported newScsJSON := c.Flags.GetOptionalString("subject-condition-set-new") // validations - if len(actionsStandard) == 0 && len(actionsCustom) == 0 { - cli.ExitWithError("At least one Standard or Custom Action [--action-standard, --action-custom] is required", nil) - } - if len(actionsStandard) > 0 { - for _, a := range actionsStandard { - a = strings.ToUpper(a) - if a != actionStandardDecrypt && a != actionStandardTransmit { - cli.ExitWithError(fmt.Sprintf("Invalid Standard Action: '%s'. Must be one of [DECRYPT, TRANSMIT].", a), nil) - } - } + if len(actionFlagValues) == 0 { + cli.ExitWithError("At least one Action [--action] is required", nil) } if existingSCSId == "" && newScsJSON == "" { cli.ExitWithError("At least one Subject Condition Set flag [--subject-condition-set-id, --subject-condition-set-new] must be provided", nil) } - actions := getFullActionsList(actionsStandard, actionsCustom) + actions := make([]*policy.Action, len(actionFlagValues)) + for i, a := range actionFlagValues { + action := &policy.Action{} + _, err := uuid.Parse(a) + if err != nil { + action.Name = a + } else { + action.Id = a + } + actions[i] = action + } var scs *subjectmapping.SubjectConditionSetCreate if newScsJSON != "" { @@ -147,7 +143,7 @@ func policy_createSubjectMapping(cmd *cobra.Command, args []string) { } } - mapping, err := h.CreateNewSubjectMapping(attrValueId, actions, existingSCSId, scs, getMetadataMutable(metadataLabels)) + mapping, err := h.CreateNewSubjectMapping(cmd.Context(), attrValueId, actions, existingSCSId, scs, getMetadataMutable(metadataLabels)) if err != nil { cli.ExitWithError("Failed to create subject mapping", err) } @@ -180,7 +176,7 @@ func policy_createSubjectMapping(cmd *cobra.Command, args []string) { HandleSuccess(cmd, mapping.GetId(), t, mapping) } -func policy_deleteSubjectMapping(cmd *cobra.Command, args []string) { +func policyDeleteSubjectMapping(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() @@ -188,7 +184,7 @@ func policy_deleteSubjectMapping(cmd *cobra.Command, args []string) { id := c.Flags.GetRequiredID("id") force := c.Flags.GetOptionalBool("force") - sm, err := h.GetSubjectMapping(id) + sm, err := h.GetSubjectMapping(cmd.Context(), id) if err != nil { errMsg := fmt.Sprintf("Failed to find subject mapping (%s)", id) cli.ExitWithError(errMsg, err) @@ -196,7 +192,7 @@ func policy_deleteSubjectMapping(cmd *cobra.Command, args []string) { cli.ConfirmAction(cli.ActionDelete, "subject mapping", sm.GetId(), force) - deleted, err := h.DeleteSubjectMapping(id) + deleted, err := h.DeleteSubjectMapping(cmd.Context(), id) if err != nil { errMsg := fmt.Sprintf("Failed to delete subject mapping (%s)", id) cli.ExitWithError(errMsg, err) @@ -209,28 +205,32 @@ func policy_deleteSubjectMapping(cmd *cobra.Command, args []string) { HandleSuccess(cmd, id, t, deleted) } -func policy_updateSubjectMapping(cmd *cobra.Command, args []string) { +func policyUpdateSubjectMapping(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() id := c.Flags.GetRequiredID("id") - actionsStandard = c.Flags.GetStringSlice("action-standard", actionsStandard, cli.FlagsStringSliceOptions{Min: 0}) - actionsCustom = c.Flags.GetStringSlice("action-custom", actionsCustom, cli.FlagsStringSliceOptions{Min: 0}) + actionFlagValues = c.Flags.GetStringSlice("action", actionFlagValues, cli.FlagsStringSliceOptions{Min: 0}) scsId := c.Flags.GetOptionalID("subject-condition-set-id") metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) - if len(actionsStandard) > 0 { - for _, a := range actionsStandard { - a = strings.ToUpper(a) - if a != actionStandardDecrypt && a != actionStandardTransmit { - cli.ExitWithError(fmt.Sprintf("Invalid Standard Action: '%s'. Must be one of [ENCRYPT, TRANSMIT]. Other actions must be custom.", a), nil) + var actions []*policy.Action + if len(actionFlagValues) > 0 { + for _, a := range actionFlagValues { + action := &policy.Action{} + _, err := uuid.Parse(a) + if err != nil { + action.Name = a + } else { + action.Id = a } + actions = append(actions, action) } } - actions := getFullActionsList(actionsStandard, actionsCustom) updated, err := h.UpdateSubjectMapping( + cmd.Context(), id, scsId, actions, @@ -249,7 +249,7 @@ func policy_updateSubjectMapping(cmd *cobra.Command, args []string) { HandleSuccess(cmd, id, t, updated) } -func policy_matchSubjectMappings(cmd *cobra.Command, args []string) { +func policyMatchSubjectMappings(cmd *cobra.Command, args []string) { c := cli.New(cmd, args) h := NewHandler(c) defer h.Close() @@ -271,7 +271,7 @@ func policy_matchSubjectMappings(cmd *cobra.Command, args []string) { } } - matched, err := h.MatchSubjectMappings(selectors) + matched, err := h.MatchSubjectMappings(cmd.Context(), selectors) if err != nil { cli.ExitWithError(fmt.Sprintf("Failed to match subject mappings with selectors %v", selectors), err) } @@ -313,39 +313,9 @@ func policy_matchSubjectMappings(cmd *cobra.Command, args []string) { HandleSuccess(cmd, "", t, matched) } -func getSubjectMappingMappingActionEnumFromChoice(readable string) policy.Action_StandardAction { - switch readable { - case actionStandardDecrypt: - return policy.Action_STANDARD_ACTION_DECRYPT - case actionStandardTransmit: - return policy.Action_STANDARD_ACTION_TRANSMIT - default: - return policy.Action_STANDARD_ACTION_UNSPECIFIED - } -} - -func getFullActionsList(standardActions, customActions []string) []*policy.Action { - actions := []*policy.Action{} - for _, a := range standardActions { - actions = append(actions, &policy.Action{ - Value: &policy.Action_Standard{ - Standard: getSubjectMappingMappingActionEnumFromChoice(a), - }, - }) - } - for _, a := range customActions { - actions = append(actions, &policy.Action{ - Value: &policy.Action_Custom{ - Custom: a, - }, - }) - } - return actions -} - func init() { getDoc := man.Docs.GetCommand("policy/subject-mappings/get", - man.WithRun(policy_getSubjectMapping), + man.WithRun(policyGetSubjectMapping), ) getDoc.Flags().StringP( getDoc.GetDocFlag("id").Name, @@ -355,12 +325,12 @@ func init() { ) listDoc := man.Docs.GetCommand("policy/subject-mappings/list", - man.WithRun(policy_listSubjectMappings), + man.WithRun(policyListSubjectMappings), ) injectListPaginationFlags(listDoc) createDoc := man.Docs.GetCommand("policy/subject-mappings/create", - man.WithRun(policy_createSubjectMapping), + man.WithRun(policyCreateSubjectMapping), ) createDoc.Flags().StringP( createDoc.GetDocFlag("attribute-value-id").Name, @@ -368,20 +338,29 @@ func init() { createDoc.GetDocFlag("attribute-value-id").Default, createDoc.GetDocFlag("attribute-value-id").Description, ) + // deprecated createDoc.Flags().StringSliceVarP( - &actionsStandard, + &[]string{}, createDoc.GetDocFlag("action-standard").Name, createDoc.GetDocFlag("action-standard").Shorthand, []string{}, createDoc.GetDocFlag("action-standard").Description, ) + // deprecated createDoc.Flags().StringSliceVarP( - &actionsCustom, + &[]string{}, createDoc.GetDocFlag("action-custom").Name, createDoc.GetDocFlag("action-custom").Shorthand, []string{}, createDoc.GetDocFlag("action-custom").Description, ) + createDoc.Flags().StringSliceVarP( + &actionFlagValues, + createDoc.GetDocFlag("action").Name, + createDoc.GetDocFlag("action").Shorthand, + []string{}, + createDoc.GetDocFlag("action").Description, + ) createDoc.Flags().String( createDoc.GetDocFlag("subject-condition-set-id").Name, createDoc.GetDocFlag("subject-condition-set-id").Default, @@ -395,7 +374,7 @@ func init() { injectLabelFlags(&createDoc.Command, false) updateDoc := man.Docs.GetCommand("policy/subject-mappings/update", - man.WithRun(policy_updateSubjectMapping), + man.WithRun(policyUpdateSubjectMapping), ) updateDoc.Flags().StringP( updateDoc.GetDocFlag("id").Name, @@ -403,20 +382,28 @@ func init() { updateDoc.GetDocFlag("id").Default, updateDoc.GetDocFlag("id").Description, ) + // deprecated updateDoc.Flags().StringSliceVarP( - &actionsStandard, + &[]string{}, updateDoc.GetDocFlag("action-standard").Name, updateDoc.GetDocFlag("action-standard").Shorthand, []string{}, updateDoc.GetDocFlag("action-standard").Description, ) updateDoc.Flags().StringSliceVarP( - &actionsCustom, + &[]string{}, updateDoc.GetDocFlag("action-custom").Name, updateDoc.GetDocFlag("action-custom").Shorthand, []string{}, updateDoc.GetDocFlag("action-custom").Description, ) + updateDoc.Flags().StringSliceVarP( + &actionFlagValues, + updateDoc.GetDocFlag("action").Name, + updateDoc.GetDocFlag("action").Shorthand, + []string{}, + updateDoc.GetDocFlag("action").Description, + ) updateDoc.Flags().String( updateDoc.GetDocFlag("subject-condition-set-id").Name, updateDoc.GetDocFlag("subject-condition-set-id").Default, @@ -425,7 +412,7 @@ func init() { injectLabelFlags(&updateDoc.Command, true) deleteDoc := man.Docs.GetCommand("policy/subject-mappings/delete", - man.WithRun(policy_deleteSubjectMapping), + man.WithRun(policyDeleteSubjectMapping), ) deleteDoc.Flags().StringP( deleteDoc.GetDocFlag("id").Name, @@ -440,7 +427,7 @@ func init() { ) matchDoc := man.Docs.GetCommand("policy/subject-mappings/match", - man.WithRun(policy_matchSubjectMappings), + man.WithRun(policyMatchSubjectMappings), ) matchDoc.Flags().StringP( matchDoc.GetDocFlag("subject").Name, diff --git a/docs/man/policy/attributes/create.md b/docs/man/policy/attributes/create.md index 58f0c12d..6714df9b 100644 --- a/docs/man/policy/attributes/create.md +++ b/docs/man/policy/attributes/create.md @@ -41,17 +41,19 @@ and may contain hyphens and underscores between other alphanumeric characters. #### ANY_OF If an Attribute is defined with logical rule `ANY_OF`, an Entity who is mapped to `any` of the associated Values of the Attribute -on TDF'd Resource Data will be Entitled. +on TDF'd Resource Data will be Entitled to take the actions in the mapping. #### ALL_OF If an Attribute is defined with logical rule `ALL_OF`, an Entity must be mapped to `all` of the associated Values of the Attribute -on TDF'd Resource Data to be Entitled. +on TDF'd Resource Data to be Entitled to take the actions in the mapping. ### HIERARCHY If an Attribute is defined with logical rule `HIERARCHY`, an Entity must be mapped to the same level Value or a level above in hierarchy -compared to a given Value on TDF'd Resource Data. Hierarchical values are considered highest at index 0 and lowest at the last index. +compared to a given Value on TDF'd Resource Data. Hierarchical values are considered highest at index 0 and lowest at the last index. Actions +propagate down through the hierarchy, so a mapping of a `read` action on the highest level Value on the Attribute will entitle the action +to each hierarchically lower value, and so on. For more general information about attributes, see the `attributes` subcommand. diff --git a/docs/man/policy/subject-mappings/_index.md b/docs/man/policy/subject-mappings/_index.md index 4b6d01ec..edf99d81 100644 --- a/docs/man/policy/subject-mappings/_index.md +++ b/docs/man/policy/subject-mappings/_index.md @@ -9,10 +9,11 @@ command: - subject-mapping --- -As data is bound to fully qualified Attribute Values when encrypted within a TDF, Entities are entitled to Attribute Values through a mechanism called Subject Mappings. +As data is bound to fully qualified Attribute Values when encrypted within a TDF, Entities are entitled to take actions on +resources containing Attribute Values through a mechanism called Subject Mappings. A Subject Mapping (SM) is the relation of a Subject Condition Set (SCS, see `subject-condition-sets` command) -to an Attribute Value to determine a Subject's Entitlement to an Attribute Value. +to an Attribute Value to determine a Subject's Entitlement to take various actions on an Attribute Value. Entities (Subjects, Users, Machines, etc.) are defined by a representation (Entity Representation) of their identity from an identity provider (idP). The OpenTDF Platform is not itself an idP, and it utilizes the OpenID Connect (OIDC) protocol as well as idP pluggability to rely upon an Entity store diff --git a/docs/man/policy/subject-mappings/create.md b/docs/man/policy/subject-mappings/create.md index 537631fa..8525c228 100644 --- a/docs/man/policy/subject-mappings/create.md +++ b/docs/man/policy/subject-mappings/create.md @@ -8,40 +8,32 @@ command: - c flags: - name: attribute-value-id - description: The ID of the attribute value to map to a subject set + description: The ID of the attribute value to map to a subject condition set shorthand: a required: true - default: '' - - name: action-standard - description: The standard action to map to a subject set - enum: - - DECRYPT - - TRANSMIT - shorthand: s - required: true - default: '' - - name: action-custom - description: The custom action to map to a subject set - shorthand: c - required: false - default: '' + - name: action + description: Each 'id' or 'name' of an Action to be entitled (i.e. 'create', 'read', 'update', 'delete') - name: subject-condition-set-id description: Known preexisting Subject Condition Set Id - required: true - default: '' - name: subject-condition-set-new description: JSON array of Subject Sets to create a new Subject Condition Set associated with the created Subject Mapping - required: false - default: '' - name: label description: "Optional metadata 'labels' in the format: key=value" shorthand: l - default: '' + - name: action-standard + description: Deprecated. Migrated to '--action'. + shorthand: s + - name: action-custom + description: Deprecated. Migrated to '--action'. + shorthand: c --- -The possible values for standard actions are DECRYPT and TRANSMIT. +Create a Subject Mapping to entitle an entity (via existing or new Subject Condition Set) to action(s) on an Attribute Value. + +Subject Mappings may entitle actions with standard names ('create', 'read', 'update', 'delete'), custom names, or by their +stored 'id' within policy. -Create a Subject Mapping to entitle an entity (via existing or new Subject Condition Set) to an Attribute Value. +For more information about actions, see the `actions` subcommand. For more information about subject mappings, see the `subject-mappings` subcommand. @@ -49,14 +41,14 @@ For more information about subject condition sets, see the `subject-condition-se ## Examples -Create a subject mapping linking to an existing subject condition set: +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-standard DECRYPT --subject-condition-set-id 8dc98f65-5f0a-4444-bfd1-6a818dc7b447 +otdfctl policy subject-mapping create --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 that linked to a new subject condition set: +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-standard DECRYPT --subject-condition-set-new '[ +otdfctl policy subject-mapping create --attribute-value-id 891cfe85-b381-4f85-9699-5f7dbfe2a9ab --action create --action update --subject-condition-set-new '[ { "condition_groups": [ { diff --git a/docs/man/policy/subject-mappings/update.md b/docs/man/policy/subject-mappings/update.md index f4db14e4..549f53bc 100644 --- a/docs/man/policy/subject-mappings/update.md +++ b/docs/man/policy/subject-mappings/update.md @@ -9,27 +9,20 @@ command: description: The ID of the subject mapping to update shorthand: i required: true + - name: action + description: Each 'id' or 'name' of an Action to be entitled (i.e. 'create', 'read', 'update', 'delete') - name: action-standard - description: The standard action to map to a subject set - enum: - - DECRYPT - - TRANSMIT + description: Deprecated. Migrated to '--action'. shorthand: s - required: true - default: '' - name: action-custom - description: The custom action to map to a subject set + description: Deprecated. Migrated to '--action'. shorthand: c - required: false - default: '' - name: subject-condition-set-id description: Known preexisting Subject Condition Set Id - required: true - default: '' + required: false - name: label description: "Optional metadata 'labels' in the format: key=value" shorthand: l - default: '' - name: force-replace-labels description: Destructively replace entire set of existing metadata 'labels' with any provided to this command default: false diff --git a/e2e/encrypt-decrypt.bats b/e2e/encrypt-decrypt.bats index 80880d10..e0af5269 100755 --- a/e2e/encrypt-decrypt.bats +++ b/e2e/encrypt-decrypt.bats @@ -48,7 +48,7 @@ setup_file() { jq --arg pem "$(<$RS_PUBLIC_KEY)" '.keys.assertion1.key = $pem' $SIGNED_ASSERTION_VERIFICATON_RS256 > tmp.json && mv tmp.json $SIGNED_ASSERTION_VERIFICATON_RS256 - SM=$(./otdfctl --host $HOST $WITH_CREDS $DEBUG_LEVEL policy subject-mappings create --action-standard DECRYPT -a "$VAL_ID" --subject-condition-set-new "$SCS") + SM=$(./otdfctl --host $HOST $WITH_CREDS $DEBUG_LEVEL policy subject-mappings create --action 'read' -a "$VAL_ID" --subject-condition-set-new "$SCS") export FQN="https://testing-enc-dec.io/attr/attr1/value/value1" export MIXED_CASE_FQN="https://Testing-Enc-Dec.io/attr/Attr1/value/VALUE1" } diff --git a/e2e/subject-condition-sets.bats b/e2e/subject-condition-sets.bats index 283d2f5e..9f974a11 100755 --- a/e2e/subject-condition-sets.bats +++ b/e2e/subject-condition-sets.bats @@ -138,7 +138,7 @@ teardown_file() { 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 + run ./otdfctl policy sm create --action 'delete' -a "$VAL_ID" --subject-condition-set-id "$MAPPED_ID" $HOST $WITH_CREDS assert_success run_otdfctl_scs prune --force diff --git a/e2e/subject-mapping.bats b/e2e/subject-mapping.bats index b6dba688..ca6bac3e 100755 --- a/e2e/subject-mapping.bats +++ b/e2e/subject-mapping.bats @@ -16,6 +16,11 @@ setup_file() { export SCS_1='[{"condition_groups":[{"conditions":[{"operator":1,"subject_external_values":["ShinyThing"],"subject_external_selector_value":".team.name"},{"operator":2,"subject_external_values":["marketing"],"subject_external_selector_value":".org.name"}],"boolean_operator":1}]}]' export SCS_2='[{"condition_groups":[{"conditions":[{"operator":2,"subject_external_values":["CoolTool","RadService"],"subject_external_selector_value":".team.name"},{"operator":1,"subject_external_values":["sales"],"subject_external_selector_value":".org.name"}],"boolean_operator":2}]}]' + + export ACTION_READ_NAME='read' + export ACTION_READ_ID=$(./otdfctl $HOST $WITH_CREDS policy actions get --name "$ACTION_READ_NAME" --json | jq -r '.id') + export ACTION_CREATE_NAME='create' + export ACTION_CREATE_ID=$(./otdfctl $HOST $WITH_CREDS policy actions get --name "$ACTION_CREATE_NAME" --json | jq -r '.id') } setup() { @@ -36,35 +41,29 @@ teardown_file() { unset HOST WITH_CREDS VAL1_ID VAL2_ID NS_ID SCS_1 SCS_2 } -# skip while policy actions rework remains in flight @test "Create subject mapping" { - skip # create with simultaneous new SCS - run ./otdfctl $HOST $WITH_CREDS policy subject-mappings create -a "$VAL1_ID" -s TRANSMIT --action-standard DECRYPT --subject-condition-set-new "$SCS_2" + run ./otdfctl $HOST $WITH_CREDS policy subject-mappings create -a "$VAL1_ID" --action "$ACTION_CREATE_NAME" --action "$ACTION_READ_NAME" --subject-condition-set-new "$SCS_2" assert_success assert_output --partial "Subject Condition Set: Id" - assert_output --partial '"Standard":1' - assert_output --partial '"Standard":2' assert_output --partial ".team.name" assert_line --regexp "Attribute Value Id.*$VAL1_ID" # scs is required - run_otdfctl_sm create --attribute-value-id "$VAL2_ID" -s TRANSMIT + run_otdfctl_sm create --attribute-value-id "$VAL2_ID" --action "$ACTION_CREATE_NAME" assert_failure assert_output --partial "At least one Subject Condition Set flag [--subject-condition-set-id, --subject-condition-set-new] must be provided" # action is required run_otdfctl_sm create -a "$VAL1_ID" --subject-condition-set-new "$SCS_2" assert_failure - assert_output --partial "At least one Standard or Custom Action [--action-standard, --action-custom] is required" + assert_output --partial "At least one Action [--action] is required" } -# skip while policy actions rework remains in flight @test "Match subject mapping" { - skip # create with simultaneous new SCS NEW_SCS='[{"condition_groups":[{"conditions":[{"operator":1,"subject_external_values":["sales"],"subject_external_selector_value":".department"}],"boolean_operator":2}]}]' - NEW_SM_ID=$(./otdfctl $HOST $WITH_CREDS policy subject-mappings create -a "$VAL2_ID" --action-standard DECRYPT --subject-condition-set-new "$NEW_SCS" --json | jq -r '.id') + NEW_SM_ID=$(./otdfctl $HOST $WITH_CREDS policy subject-mappings create -a "$VAL2_ID" --action "$ACTION_READ_NAME" --subject-condition-set-new "$NEW_SCS" --json | jq -r '.id') run_otdfctl_sm match -x '.department' assert_success @@ -95,11 +94,9 @@ teardown_file() { refute_output --partial "$NEW_SM_ID" } -# skip while policy actions rework remains in flight @test "Get subject mapping" { - skip new_scs=$(./otdfctl $HOST $WITH_CREDS policy scs create -s "$SCS_2" --json | jq -r '.id') - created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL2_ID" -s TRANSMIT --subject-condition-set-id "$new_scs" --json | jq -r '.id') + created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL2_ID" --action "$ACTION_CREATE_ID" --subject-condition-set-id "$new_scs" --json | jq -r '.id') # table run_otdfctl_sm get --id "$created" assert_success @@ -114,19 +111,20 @@ teardown_file() { [ "$(echo $output | jq -r '.id')" = "$created" ] [ "$(echo $output | jq -r '.attribute_value.id')" = "$VAL2_ID" ] [ "$(echo $output | jq -r '.subject_condition_set.id')" = "$new_scs" ] + [ "$(echo $output | jq -r '.actions[0].id')" = "$ACTION_CREATE_ID" ] + [ "$(echo $output | jq -r '.actions[0].name')" = "$ACTION_CREATE_NAME" ] } -# skip while policy actions rework remains in flight @test "Update a subject mapping" { - skip - created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL1_ID" -s DECRYPT --subject-condition-set-new "$SCS_1" --json | jq -r '.id') + created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL1_ID" --action "$ACTION_READ_NAME" --subject-condition-set-new "$SCS_1" --json | jq -r '.id') additional_scs=$(./otdfctl $HOST $WITH_CREDS policy scs create -s "$SCS_2" --json | jq -r '.id') # replace the action (always destructive replacement) - run_otdfctl_sm update --id "$created" -s TRANSMIT --json + run_otdfctl_sm update --id "$created" --action "$ACTION_CREATE_NAME" --json assert_success [ "$(echo $output | jq -r '.id')" = "$created" ] - [ "$(echo $output | jq -r '.actions[0].Value.Standard')" = 2 ] + [ "$(echo $output | jq -r '.actions[0].name')" = "$ACTION_CREATE_NAME" ] + [ "$(echo $output | jq -r '.actions[0].id')" = "$ACTION_CREATE_ID" ] # reassign the SCS being mapped to run_otdfctl_sm update --id "$created" --subject-condition-set-id "$additional_scs" --json @@ -135,10 +133,8 @@ teardown_file() { [ "$(echo $output | jq -r '.subject_condition_set.id')" = "$additional_scs" ] } -# skip while policy actions rework remains in flight @test "List subject mappings" { - skip - created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL1_ID" -s TRANSMIT --subject-condition-set-new "$SCS_2" --json | jq -r '.id') + created=$(./otdfctl $HOST $WITH_CREDS policy sm create -a "$VAL1_ID" --action "$ACTION_CREATE_NAME" --subject-condition-set-new "$SCS_2" --json | jq -r '.id') run_otdfctl_sm list assert_success diff --git a/pkg/handlers/subjectmappings.go b/pkg/handlers/subjectmappings.go index 06043d37..c7150e1c 100644 --- a/pkg/handlers/subjectmappings.go +++ b/pkg/handlers/subjectmappings.go @@ -1,6 +1,8 @@ package handlers import ( + "context" + "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/protocol/go/policy/subjectmapping" @@ -15,15 +17,15 @@ const ( var SubjectMappingOperatorEnumChoices = []string{SubjectMappingOperatorIn, SubjectMappingOperatorNotIn, SubjectMappingOperatorUnspecified} -func (h Handler) GetSubjectMapping(id string) (*policy.SubjectMapping, error) { - resp, err := h.sdk.SubjectMapping.GetSubjectMapping(h.ctx, &subjectmapping.GetSubjectMappingRequest{ +func (h Handler) GetSubjectMapping(ctx context.Context, id string) (*policy.SubjectMapping, error) { + resp, err := h.sdk.SubjectMapping.GetSubjectMapping(ctx, &subjectmapping.GetSubjectMappingRequest{ Id: id, }) return resp.GetSubjectMapping(), err } -func (h Handler) ListSubjectMappings(limit, offset int32) ([]*policy.SubjectMapping, *policy.PageResponse, error) { - resp, err := h.sdk.SubjectMapping.ListSubjectMappings(h.ctx, &subjectmapping.ListSubjectMappingsRequest{ +func (h Handler) ListSubjectMappings(ctx context.Context, limit, offset int32) ([]*policy.SubjectMapping, *policy.PageResponse, error) { + resp, err := h.sdk.SubjectMapping.ListSubjectMappings(ctx, &subjectmapping.ListSubjectMappingsRequest{ Pagination: &policy.PageRequest{ Limit: limit, Offset: offset, @@ -36,9 +38,9 @@ func (h Handler) ListSubjectMappings(limit, offset int32) ([]*policy.SubjectMapp } // Creates and returns the created subject mapping -func (h Handler) CreateNewSubjectMapping(attrValId string, actions []*policy.Action, existingSCSId string, newScs *subjectmapping.SubjectConditionSetCreate, m *common.MetadataMutable) (*policy.SubjectMapping, error) { - resp, err := h.sdk.SubjectMapping.CreateSubjectMapping(h.ctx, &subjectmapping.CreateSubjectMappingRequest{ - AttributeValueId: attrValId, +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{ + AttributeValueId: attrValID, Actions: actions, ExistingSubjectConditionSetId: existingSCSId, NewSubjectConditionSet: newScs, @@ -47,11 +49,11 @@ func (h Handler) CreateNewSubjectMapping(attrValId string, actions []*policy.Act if err != nil { return nil, err } - return h.GetSubjectMapping(resp.GetSubjectMapping().GetId()) + return h.GetSubjectMapping(ctx, resp.GetSubjectMapping().GetId()) } // Updates and returns the updated subject mapping -func (h Handler) UpdateSubjectMapping(id string, updatedSCSId string, updatedActions []*policy.Action, metadata *common.MetadataMutable, metadataBehavior common.MetadataUpdateEnum) (*policy.SubjectMapping, error) { +func (h Handler) UpdateSubjectMapping(ctx context.Context, id string, updatedSCSId string, updatedActions []*policy.Action, metadata *common.MetadataMutable, metadataBehavior common.MetadataUpdateEnum) (*policy.SubjectMapping, error) { _, err := h.sdk.SubjectMapping.UpdateSubjectMapping(h.ctx, &subjectmapping.UpdateSubjectMappingRequest{ Id: id, SubjectConditionSetId: updatedSCSId, @@ -62,24 +64,24 @@ func (h Handler) UpdateSubjectMapping(id string, updatedSCSId string, updatedAct if err != nil { return nil, err } - return h.GetSubjectMapping(id) + return h.GetSubjectMapping(ctx, id) } -func (h Handler) DeleteSubjectMapping(id string) (*policy.SubjectMapping, error) { - resp, err := h.sdk.SubjectMapping.DeleteSubjectMapping(h.ctx, &subjectmapping.DeleteSubjectMappingRequest{ +func (h Handler) DeleteSubjectMapping(ctx context.Context, id string) (*policy.SubjectMapping, error) { + resp, err := h.sdk.SubjectMapping.DeleteSubjectMapping(ctx, &subjectmapping.DeleteSubjectMappingRequest{ Id: id, }) return resp.GetSubjectMapping(), err } -func (h Handler) MatchSubjectMappings(selectors []string) ([]*policy.SubjectMapping, error) { +func (h Handler) MatchSubjectMappings(ctx context.Context, selectors []string) ([]*policy.SubjectMapping, error) { subjectProperties := make([]*policy.SubjectProperty, len(selectors)) for i, selector := range selectors { subjectProperties[i] = &policy.SubjectProperty{ ExternalSelectorValue: selector, } } - resp, err := h.sdk.SubjectMapping.MatchSubjectMappings(h.ctx, &subjectmapping.MatchSubjectMappingsRequest{ + resp, err := h.sdk.SubjectMapping.MatchSubjectMappings(ctx, &subjectmapping.MatchSubjectMappingsRequest{ SubjectProperties: subjectProperties, }) return resp.GetSubjectMappings(), err