diff --git a/cmd/policy/actions.go b/cmd/policy/actions.go index 0d68e27a..fcd5213b 100644 --- a/cmd/policy/actions.go +++ b/cmd/policy/actions.go @@ -17,12 +17,14 @@ func policyGetAction(cmd *cobra.Command, args []string) { id := c.Flags.GetOptionalID("id") name := c.Flags.GetOptionalString("name") + // TODO: switch to required namespace if id not provided once namespacing is required by policy + namespace := c.Flags.GetOptionalString("namespace") if id == "" && name == "" { cli.ExitWithError("Either 'id' or 'name' must be provided", nil) } - action, err := h.GetAction(cmd.Context(), id, name) + action, err := h.GetAction(cmd.Context(), id, name, namespace) if err != nil { identifier := fmt.Sprintf("id: %s", id) if id == "" { @@ -35,6 +37,7 @@ func policyGetAction(cmd *cobra.Command, args []string) { rows := [][]string{ {"Id", action.GetId()}, {"Name", action.GetName()}, + {"Namespace", action.GetNamespace().GetFqn()}, } if mdRows := getMetadataRows(action.GetMetadata()); mdRows != nil { rows = append(rows, mdRows...) @@ -51,8 +54,9 @@ func policyListActions(cmd *cobra.Command, args []string) { limit := c.Flags.GetRequiredInt32("limit") offset := c.Flags.GetRequiredInt32("offset") + namespace := c.Flags.GetOptionalString("namespace") - resp, err := h.ListActions(cmd.Context(), limit, offset) + resp, err := h.ListActions(cmd.Context(), limit, offset, namespace) if err != nil { cli.ExitWithError("Failed to list actions", err) } @@ -60,6 +64,7 @@ func policyListActions(cmd *cobra.Command, args []string) { cli.NewUUIDColumn(), table.NewFlexColumn("name", "Name", cli.FlexColumnWidthFour), table.NewFlexColumn("action_type", "Action Type", cli.FlexColumnWidthFour), + table.NewFlexColumn("namespace", "Namespace", cli.FlexColumnWidthFour), ) rows := []table.Row{} for _, a := range resp.GetActionsStandard() { @@ -67,13 +72,16 @@ func policyListActions(cmd *cobra.Command, args []string) { "id": a.GetId(), "action_type": "standard", "name": a.GetName(), + "namespace": a.GetNamespace().GetFqn(), })) } + for _, a := range resp.GetActionsCustom() { rows = append(rows, table.NewRow(table.RowData{ "id": a.GetId(), "action_type": "custom", "name": a.GetName(), + "namespace": a.GetNamespace().GetFqn(), })) } @@ -88,9 +96,10 @@ func policyCreateAction(cmd *cobra.Command, args []string) { defer h.Close() name := c.Flags.GetRequiredString("name") + namespace := c.Flags.GetOptionalString("namespace") metadataLabels = c.Flags.GetStringSlice("label", metadataLabels, cli.FlagsStringSliceOptions{Min: 0}) - action, err := h.CreateAction(cmd.Context(), name, getMetadataMutable(metadataLabels)) + action, err := h.CreateAction(cmd.Context(), name, namespace, getMetadataMutable(metadataLabels)) if err != nil { cli.ExitWithError("Failed to create action", err) } @@ -98,6 +107,7 @@ func policyCreateAction(cmd *cobra.Command, args []string) { rows := [][]string{ {"Id", action.GetId()}, {"Name", action.GetName()}, + {"Namespace", action.GetNamespace().GetFqn()}, } if mdRows := getMetadataRows(action.GetMetadata()); mdRows != nil { @@ -117,7 +127,7 @@ func policyDeleteAction(cmd *cobra.Command, args []string) { force := c.Flags.GetOptionalBool("force") ctx := cmd.Context() - action, err := h.GetAction(ctx, id, "") + action, err := h.GetAction(ctx, id, "", "") if err != nil { errMsg := fmt.Sprintf("Failed to find action (%s)", id) cli.ExitWithError(errMsg, err) @@ -130,7 +140,11 @@ func policyDeleteAction(cmd *cobra.Command, args []string) { errMsg := fmt.Sprintf("Failed to delete action (%s)", id) cli.ExitWithError(errMsg, err) } - rows := [][]string{{"Id", id}, {"Name", action.GetName()}} + rows := [][]string{ + {"Id", id}, + {"Name", action.GetName()}, + {"Namespace", action.GetNamespace().GetFqn()}, + } if mdRows := getMetadataRows(action.GetMetadata()); mdRows != nil { rows = append(rows, mdRows...) } @@ -157,7 +171,11 @@ func policyUpdateAction(cmd *cobra.Command, args []string) { if err != nil { cli.ExitWithError("Failed to update action", err) } - rows := [][]string{{"Id", id}, {"Name", updated.GetName()}} + rows := [][]string{ + {"Id", id}, + {"Name", updated.GetName()}, + {"Namespace", updated.GetNamespace().GetFqn()}, + } if mdRows := getMetadataRows(updated.GetMetadata()); mdRows != nil { rows = append(rows, mdRows...) } @@ -166,6 +184,15 @@ func policyUpdateAction(cmd *cobra.Command, args []string) { common.HandleSuccess(cmd, id, t, updated) } +func injectNamespaceFlag(doc *man.Doc) { + doc.Flags().StringP( + doc.GetDocFlag("namespace").Name, + doc.GetDocFlag("namespace").Shorthand, + doc.GetDocFlag("namespace").Default, + doc.GetDocFlag("namespace").Description, + ) +} + func initActionsCommands() { getDoc := man.Docs.GetCommand("policy/actions/get", man.WithRun(policyGetAction), @@ -182,10 +209,12 @@ func initActionsCommands() { getDoc.GetDocFlag("name").Default, getDoc.GetDocFlag("name").Description, ) + injectNamespaceFlag(getDoc) listDoc := man.Docs.GetCommand("policy/actions/list", man.WithRun(policyListActions), ) + injectNamespaceFlag(listDoc) injectListPaginationFlags(listDoc) createDoc := man.Docs.GetCommand("policy/actions/create", @@ -197,6 +226,7 @@ func initActionsCommands() { createDoc.GetDocFlag("name").Default, createDoc.GetDocFlag("name").Description, ) + injectNamespaceFlag(createDoc) injectLabelFlags(&createDoc.Command, false) updateDoc := man.Docs.GetCommand("policy/actions/update", diff --git a/docs/man/policy/actions/_index.md b/docs/man/policy/actions/_index.md index 438c5c54..9c2f1725 100644 --- a/docs/man/policy/actions/_index.md +++ b/docs/man/policy/actions/_index.md @@ -16,11 +16,11 @@ Standard Actions in Policy are comprised of the below, and only their metadata l - update - delete -Custom Actions known to Policy are admin-defined, globally unique (not namespaced), and will be lower +Custom Actions known to Policy are admin-defined, unique within a namespace, and will be lower cased when stored. They may contain underscores (`_`) or hyphens (`-`) if preceded or followed by an alphanumeric character. For example: - download - queue-to-print - send_email -For more information about entitlement and Subject Mappings, see the `subject-mappings` command. \ No newline at end of file +For more information about entitlement and Subject Mappings, see the `subject-mappings` command. diff --git a/docs/man/policy/actions/create.md b/docs/man/policy/actions/create.md index c7025d34..b20c6650 100644 --- a/docs/man/policy/actions/create.md +++ b/docs/man/policy/actions/create.md @@ -9,8 +9,11 @@ command: flags: - name: name shorthand: n - description: Name of the custom action (must be unique within Policy) + description: Name of the custom action (must be unique within a namespace) required: true + - name: namespace + shorthand: s + description: Namespace ID or FQN - name: label description: "Optional metadata 'labels' in the format: key=value" shorthand: l @@ -20,8 +23,7 @@ command: Add a custom `action` to the platform Policy. An Action `name` is normalized to lower case and may contain underscores (`_`) or hyphens (`-`) -between other alphanumeric characters. Each name must be globally unique as actions are not -namespaced. +between other alphanumeric characters. Each name must be unique within a namespace. For more information, see the `actions` subcommand. @@ -30,6 +32,5 @@ For more information, see the `actions` subcommand. Create a custom action named 'install_package': ```shell -otdfctl policy actions create --name install_package +otdfctl policy actions create --name install_package --namespace https://example.com ``` - diff --git a/docs/man/policy/actions/get.md b/docs/man/policy/actions/get.md index 0c451224..4ea0a107 100644 --- a/docs/man/policy/actions/get.md +++ b/docs/man/policy/actions/get.md @@ -11,10 +11,15 @@ command: - name: name shorthand: n description: Name of the action + - name: namespace + shorthand: s + description: Namespace ID or FQN --- If both `id` and `name` flag values are provided, `id` is preferred. +When using `--name`, `--namespace` is required. + For more information about Actions, see the manual for the `actions` subcommand. ## Example @@ -28,5 +33,5 @@ otdfctl policy actions get --id e1402c63-eeaa-45e2-85d2-b939d135941f Get by Name: ```shell -otdfctl policy actions get --name read +otdfctl policy actions get --name read --namespace https://example.com ``` diff --git a/docs/man/policy/actions/list.md b/docs/man/policy/actions/list.md index fc41472d..76e5e987 100644 --- a/docs/man/policy/actions/list.md +++ b/docs/man/policy/actions/list.md @@ -5,6 +5,9 @@ command: aliases: - l flags: + - name: namespace + shorthand: s + description: Namespace ID or FQN - name: limit shorthand: l description: Limit retrieved count @@ -18,5 +21,5 @@ For more information about Actions, see the manual for the `actions` subcommand. ## Example ```shell -otdfctl policy actions list +otdfctl policy actions list --namespace https://example.com ``` diff --git a/docs/man/policy/actions/update.md b/docs/man/policy/actions/update.md index bcae2748..5deb0856 100644 --- a/docs/man/policy/actions/update.md +++ b/docs/man/policy/actions/update.md @@ -11,7 +11,7 @@ command: required: true - name: name shorthand: n - description: Optional updated name of the custom action (must be unique within Policy) + description: Optional updated name of the custom action (must be unique within a namespace) - name: label description: "Optional metadata 'labels' in the format: key=value" shorthand: l diff --git a/e2e/actions.bats b/e2e/actions.bats index b8f2b609..95411cc2 100644 --- a/e2e/actions.bats +++ b/e2e/actions.bats @@ -3,11 +3,12 @@ # Tests for actions setup_file() { - # TODO: Remove this file-level skip once otdfctl passes namespace flags for the namespaced action APIs. - skip "Temporarily disabled [namespaced-actions]: platform actions APIs now require namespace flags" - export WITH_CREDS='--with-client-creds-file ./creds.json' export HOST='--host http://localhost:8080' + export ACTION_NAMESPACE_NAME='test-act.org' + export ACTION_NAMESPACE="https://$ACTION_NAMESPACE_NAME" + # create namespace first (needed for action creation) + export NS_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes namespaces create --name "$ACTION_NAMESPACE_NAME" --json | jq -r '.id') } setup() { @@ -22,58 +23,112 @@ setup() { teardown_file() { # clear out all test env vars - unset HOST WITH_CREDS + # remove the namespace and cascade delete attributes and values used in registered resource values tests + ./otdfctl $HOST $WITH_CREDS policy attributes namespaces unsafe delete --id "$NS_ID" --force + unset HOST WITH_CREDS ACTION_NAMESPACE ACTION_NAMESPACE_NAME NS_ID } + @test "Create a new custom action - Good" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" + # with a namespace + run_otdfctl_action create --name test_action_create_namespaced --namespace "$ACTION_NAMESPACE" + assert_output --partial "SUCCESS" + assert_line --regexp "Name.*test_action_create_namespaced" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" + assert_output --partial "Id" + assert_output --partial "Created At" + assert_line --partial "Updated At" + + # cleanup + created_id=$(echo "$output" | grep Id | awk -F'│' '{print $3}' | xargs) + run_otdfctl_action delete --id $created_id --force + + # without a namespace (should default to un-namespaced) run_otdfctl_action create --name test_action_create assert_output --partial "SUCCESS" assert_line --regexp "Name.*test_action_create" assert_output --partial "Id" assert_output --partial "Created At" assert_line --partial "Updated At" + # ensure namespace is empty for un-namespaced actions + refute_line --regexp "Namespace.*$ACTION_NAMESPACE" - # cleanup created_id=$(echo "$output" | grep Id | awk -F'│' '{print $3}' | xargs) run_otdfctl_action delete --id $created_id --force } @test "Create a new action - Bad" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" # bad action names - run_otdfctl_action create --name ends_underscored_ + run_otdfctl_action create --name ends_underscored_ --namespace "$ACTION_NAMESPACE" assert_failure - run_otdfctl_action create --name -first-char-hyphen + run_otdfctl_action create --name -first-char-hyphen --namespace "$ACTION_NAMESPACE" assert_failure - run_otdfctl_action create --name inval!d.chars + run_otdfctl_action create --name inval!d.chars --namespace "$ACTION_NAMESPACE" assert_failure # missing flag - run_otdfctl_action create + run_otdfctl_action create --namespace "$ACTION_NAMESPACE" assert_failure assert_output --partial "Flag '--name' is required" + + # TODO: re-enable when namespace is required + # run_otdfctl_action create --name no_namespace + # assert_failure + # assert_output --partial "Flag '--namespace' is required" # conflict + run_otdfctl_action create -n "read" --namespace "$ACTION_NAMESPACE" + assert_failure + assert_output --partial "intended action would violate a restriction" + run_otdfctl_action create -n "read" + assert_failure + assert_output --partial "intended action would violate a restriction" + + # duplicate custom action + run_otdfctl_action create --name test_action_conflict --namespace "$ACTION_NAMESPACE" --json + assert_success + conflict_action_id=$(echo "$output" | jq -er '.id') + assert_success + [ -n "$conflict_action_id" ] + + run_otdfctl_action create --name test_action_conflict --namespace "$ACTION_NAMESPACE" assert_failure assert_output --partial "already_exists" + + # cleanup + run_otdfctl_action delete --id "$conflict_action_id" --force } @test "Get an action - Good" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" - run_otdfctl_action get --name "read" + run_otdfctl_action get --name "read" --namespace "$ACTION_NAMESPACE" assert_success assert_line --partial "Id" assert_line --regexp "Name.*read" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" # get by name to retrieve the ID - UPDATE_ACTION_ID=$(./otdfctl policy actions get --name update --json $HOST $WITH_CREDS | jq -r '.id') + UPDATE_ACTION_ID=$(./otdfctl policy actions get --name update --namespace "$ACTION_NAMESPACE" --json $HOST $WITH_CREDS | jq -r '.id') + # ensure getting by id does not require namespace run_otdfctl_action get --id "$UPDATE_ACTION_ID" --json assert_success [ "$(echo "$output" | jq -r '.id')" = "$UPDATE_ACTION_ID" ] [ "$(echo "$output" | jq -r '.name')" = "update" ] + + # ensure you can use the namespace id instead of the fqn + run_otdfctl_action get --name "read" --namespace "$NS_ID" + assert_success + assert_line --partial "Id" + assert_line --regexp "Name.*read" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" + + # ensure get without namespace still works for un-namespaced actions + run_otdfctl_action get --name "read" + assert_success + assert_line --partial "Id" + assert_line --regexp "Name.*read" + refute_line --regexp "Namespace.*$ACTION_NAMESPACE" } @test "Get an action - Bad" { @@ -84,19 +139,34 @@ teardown_file() { run_otdfctl_action get --id 'testing_get' assert_failure assert_output --partial "must be a valid UUID" + + # TODO: re-enable when namespace is required + # run_otdfctl_action get --name 'testing_get' + # assert_failure + # assert_output --partial "namespace' must be provided when using 'name'" } @test "List actions" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" - run_otdfctl_action list + run_otdfctl_action create --name test_action_list_namespaced --namespace "$ACTION_NAMESPACE" --json + assert_success + created_id=$(echo "$output" | jq -r '.id') + run_otdfctl_action create --name test_action_list_unnamespaced --json + assert_success + created_id_2=$(echo "$output" | jq -r '.id') + + run_otdfctl_action list --namespace "$ACTION_NAMESPACE" + assert_output --partial "Namespace" + assert_output --partial "$ACTION_NAMESPACE" assert_output --partial "create" assert_output --partial "read" assert_output --partial "update" assert_output --partial "delete" assert_output --partial "Total" assert_line --regexp "Current Offset.*0" + assert_output --partial "test_action_list_namespaced" + refute_output --partial "test_action_list_unnamespaced" - run_otdfctl_action list --json + run_otdfctl_action list --namespace "$ACTION_NAMESPACE" --json assert_success assert_not_equal $(echo "$output" | jq -r 'pagination') "null" assert_output --partial "create" @@ -105,16 +175,30 @@ teardown_file() { assert_output --partial "delete" total=$(echo "$output" | jq -r '.pagination.total') [[ "$total" -ge 1 ]] + + # listing without namespace should succeed and should include both namespaced and un-namespaced actions (namespace field should be empty for un-namespaced actions) + run_otdfctl_action list + assert_output --partial "create" + assert_output --partial "read" + assert_output --partial "update" + assert_output --partial "delete" + assert_output --partial "Total" + assert_line --regexp "Current Offset.*0" + assert_output --partial "test_action_list_namespaced" + assert_output --partial "test_action_list_unnamespaced" + + run_otdfctl_action delete --id $created_id --force + run_otdfctl_action delete --id $created_id_2 --force } @test "Update action" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" - ACTION_TO_UPDATE=$(./otdfctl policy actions create --name testing_updation $HOST $WITH_CREDS --json | jq -r '.id') + ACTION_TO_UPDATE=$(./otdfctl policy actions create --name testing_updation --namespace "$ACTION_NAMESPACE" $HOST $WITH_CREDS --json | jq -r '.id') # extend labels run_otdfctl_action update --id "$ACTION_TO_UPDATE" -l key=value --label test=true assert_success assert_line --regexp "Id.*$ACTION_TO_UPDATE" assert_line --regexp "Name.*testing_updation" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" assert_line --regexp "Labels.*key: value" assert_line --regexp "Labels.*test: true" @@ -123,6 +207,7 @@ teardown_file() { assert_success assert_line --regexp "Id.*$ACTION_TO_UPDATE" assert_line --regexp "Name.*testing_updation" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" assert_line --regexp "Labels.*key: other" refute_output --regexp "Labels.*key: value" refute_output --regexp "Labels.*test: true" @@ -133,6 +218,7 @@ teardown_file() { assert_success assert_line --regexp "Id.*$ACTION_TO_UPDATE" assert_line --regexp "Name.*updated_action_in_test" + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" refute_output --regexp "Name.*testing_updation" # clean up @@ -140,14 +226,14 @@ teardown_file() { } @test "Delete action - bad" { - STANDARD_ACTION=$(./otdfctl policy actions get --name update $HOST $WITH_CREDS --json | jq -r '.id') + STANDARD_ACTION=$(./otdfctl policy actions get --name update --namespace "$ACTION_NAMESPACE" $HOST $WITH_CREDS --json | jq -r '.id') run_otdfctl_action delete --id "$STANDARD_ACTION" --force assert_failure } @test "Delete action - good" { - skip "Temporarily disabled [namespaced-actions]: actions now require namespace flags" - DELETABLE_ACTION=$(./otdfctl policy actions create --name testing-delete $HOST $WITH_CREDS --json | jq -r '.id') + DELETABLE_ACTION=$(./otdfctl policy actions create --name testing-delete --namespace "$ACTION_NAMESPACE" $HOST $WITH_CREDS --json | jq -r '.id') run_otdfctl_action delete --id "$DELETABLE_ACTION" --force assert_success + assert_line --regexp "Namespace.*$ACTION_NAMESPACE" } diff --git a/e2e/obligations.bats b/e2e/obligations.bats index 07a4d1f4..fdbc9fd9 100644 --- a/e2e/obligations.bats +++ b/e2e/obligations.bats @@ -22,9 +22,9 @@ setup_file() { # create shared actions for tests export ACTION_1_NAME="test_action_1" - export ACTION_1_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$ACTION_1_NAME" --json | jq -r '.id') + export ACTION_1_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$ACTION_1_NAME" --namespace "$NS_ID" --json | jq -r '.id') export ACTION_2_NAME="test_action_2" - export ACTION_2_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$ACTION_2_NAME" --json | jq -r '.id') + export ACTION_2_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$ACTION_2_NAME" --namespace "$NS_ID" --json | jq -r '.id') # create shared attributes for tests export ATTR_NAME="test_attr_for_triggers" diff --git a/e2e/registered-resources.bats b/e2e/registered-resources.bats index ea852953..8cf046a8 100644 --- a/e2e/registered-resources.bats +++ b/e2e/registered-resources.bats @@ -3,33 +3,39 @@ # Tests for registered resources setup_file() { - # TODO: Remove this file-level skip once otdfctl passes namespace flags for the namespaced action APIs used by registered resource values. - skip "Temporarily disabled [namespaced-actions]: registered resource BATS setup still depends on pre-namespace action APIs" - export WITH_CREDS='--with-client-creds-file ./creds.json' export HOST='--host http://localhost:8080' # create namespace first (needed for registered resource creation) export NS_NAME="test-rr.org" - export NS_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes namespaces create --name "$NS_NAME" --json | jq -r '.id') + NS_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes namespaces create --name "$NS_NAME" --json | jq -r '.id') + export NS_ID # create registered resource used in registered resource values tests export RR_NAME="test_rr_for_values" - export RR_ID=$(./otdfctl $HOST $WITH_CREDS policy registered-resources create --name "$RR_NAME" --namespace "$NS_ID" --json | jq -r '.id') + RR_ID=$(./otdfctl $HOST $WITH_CREDS policy registered-resources create --name "$RR_NAME" --namespace "$NS_ID" --json | jq -r '.id') + export RR_ID # create custom action to be used in registered resource values tests export CUSTOM_ACTION_NAME="test_action_for_values" - export CUSTOM_ACTION_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$CUSTOM_ACTION_NAME" --json | jq -r '.id') + CUSTOM_ACTION_ID=$(./otdfctl $HOST $WITH_CREDS policy actions create --name "$CUSTOM_ACTION_NAME" --namespace "$NS_ID" --json | jq -r '.id') + export CUSTOM_ACTION_ID # get standard read action id to use in registered resource values tests + # TODO: when RRs support passing the namespace down to the action, add --namespace "$NS_ID" export READ_ACTION_NAME="read" - export READ_ACTION_ID=$(./otdfctl $HOST $WITH_CREDS policy actions get --name "$READ_ACTION_NAME" --json | jq -r '.id') + READ_ACTION_ID=$(./otdfctl $HOST $WITH_CREDS policy actions get --name "$READ_ACTION_NAME" --json | jq -r '.id') + export READ_ACTION_ID export ATTR_NAME=test_rr_attr attr_id=$(./otdfctl $HOST $WITH_CREDS policy attributes create --namespace "$NS_ID" --name "$ATTR_NAME" --rule ANY_OF -l key=value --json | jq -r '.id') - export ATTR_VAL_1_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes values create --attribute-id "$attr_id" --value test_reg_res_attr__val_1 --json | jq -r '.id') - export ATTR_VAL_1_FQN=$(./otdfctl $HOST $WITH_CREDS policy attributes values get --id "$ATTR_VAL_1_ID" --json | jq -r '.fqn') - export ATTR_VAL_2_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes values create --attribute-id "$attr_id" --value test_reg_res_attr__val_2 --json | jq -r '.id') - export ATTR_VAL_2_FQN=$(./otdfctl $HOST $WITH_CREDS policy attributes values get --id "$ATTR_VAL_2_ID" --json | jq -r '.fqn') + ATTR_VAL_1_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes values create --attribute-id "$attr_id" --value test_reg_res_attr__val_1 --json | jq -r '.id') + export ATTR_VAL_1_ID + ATTR_VAL_1_FQN=$(./otdfctl $HOST $WITH_CREDS policy attributes values get --id "$ATTR_VAL_1_ID" --json | jq -r '.fqn') + export ATTR_VAL_1_FQN + ATTR_VAL_2_ID=$(./otdfctl $HOST $WITH_CREDS policy attributes values create --attribute-id "$attr_id" --value test_reg_res_attr__val_2 --json | jq -r '.id') + export ATTR_VAL_2_ID + ATTR_VAL_2_FQN=$(./otdfctl $HOST $WITH_CREDS policy attributes values get --id "$ATTR_VAL_2_ID" --json | jq -r '.fqn') + export ATTR_VAL_2_FQN echo "FQN: $ATTR_VAL_1_FQN" } @@ -227,7 +233,6 @@ teardown_file() { # Tests for registered resource values @test "Create a registered resource value - Good" { - skip "Temporarily disabled [namespaced-actions]: action-name validation/path is failing in CI" # simple by resource ID run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_create_rr_val assert_output --partial "SUCCESS" @@ -247,7 +252,8 @@ teardown_file() { created_id_simple_by_res_name=$(echo "$output" | grep Id | awk -F'│' '{print $3}' | xargs) # with action attribute values - run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_create_rr_val_with_action_attr_vals --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_FQN\"" --action-attribute-value "\"$CUSTOM_ACTION_NAME;$ATTR_VAL_2_ID\"" --json + # TODO(namespaced-actions): switch custom action identifier back to name when RR action resolution is namespaced. + run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_create_rr_val_with_action_attr_vals --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_FQN\"" --action-attribute-value "\"$CUSTOM_ACTION_ID;$ATTR_VAL_2_ID\"" --json assert_success [ "$(echo "$output" | jq -r '.id')" != "" ] [ "$(echo "$output" | jq -r '.value')" = "test_create_rr_val_with_action_attr_vals" ] @@ -301,7 +307,6 @@ teardown_file() { } @test "Get a registered resource value - Good" { - skip "Temporarily disabled [namespaced-actions]: action-name validation/path is failing in CI" # setup a resource value to get run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_get_rr_val --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_ID\"" assert_success @@ -348,7 +353,6 @@ teardown_file() { } @test "List registered resource values - Good" { - skip "Temporarily disabled [namespaced-actions]: dependent registered resource value setup is failing in CI" # setup values to list run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_list_rr_val_1 --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_ID\"" reg_res_val1_id=$(echo "$output" | grep Id | awk -F'│' '{print $3}' | xargs) @@ -400,7 +404,6 @@ teardown_file() { } @test "Update registered resource values" { - skip "Temporarily disabled [namespaced-actions]: action-name validation/path is failing in CI" # setup a resource value to update run_otdfctl_reg_res_values create --resource "$RR_ID" --value test_update_rr_val --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_ID\"" assert_success @@ -428,7 +431,8 @@ teardown_file() { [ "$(echo "$output" | jq -r 'any(.action_attribute_values[]; .action.id == "'"$READ_ACTION_ID"'" and .action.name == "'"$READ_ACTION_NAME"'" and .attribute_value.id == "'"$ATTR_VAL_1_ID"'" and .attribute_value.fqn == "'"$ATTR_VAL_1_FQN"'")')" = "true" ] # update action attribute values - run_otdfctl_reg_res_values update --id "$created_id" --action-attribute-value "\"$READ_ACTION_NAME;$ATTR_VAL_1_FQN\"" --action-attribute-value "\"$CUSTOM_ACTION_ID;$ATTR_VAL_2_ID\"" --force --json + # TODO(namespaced-actions): switch action identifiers back to names when RR action resolution is namespaced. + run_otdfctl_reg_res_values update --id "$created_id" --action-attribute-value "\"$READ_ACTION_ID;$ATTR_VAL_1_FQN\"" --action-attribute-value "\"$CUSTOM_ACTION_ID;$ATTR_VAL_2_ID\"" --force --json assert_success [ "$(echo "$output" | jq -r '.id')" = "$created_id" ] [ "$(echo "$output" | jq -r 'any(.action_attribute_values[]; .action.id == "'"$READ_ACTION_ID"'" and .action.name == "'"$READ_ACTION_NAME"'" and .attribute_value.id == "'"$ATTR_VAL_1_ID"'" and .attribute_value.fqn == "'"$ATTR_VAL_1_FQN"'")')" = "true" ] diff --git a/go.mod b/go.mod index 73679967..34f6368e 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/opentdf/platform/lib/flattening v0.1.3 github.com/opentdf/platform/lib/identifier v0.3.0 github.com/opentdf/platform/lib/ocrypto v0.10.0 - github.com/opentdf/platform/protocol/go v0.18.0 + github.com/opentdf/platform/protocol/go v0.19.0 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 e88219fb..76c3d353 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/opentdf/platform/lib/identifier v0.3.0 h1:kLVRVC8wE0kmuEIdodEhqIRym5O github.com/opentdf/platform/lib/identifier v0.3.0/go.mod h1:3RlXYY9sxH/VnMkgm/M6zryUrtBL0lnsOVyqb0hYYvs= github.com/opentdf/platform/lib/ocrypto v0.10.0 h1:7dn/z/1qH3p+gWCrfOoU7hj9XF/p5N+b2JBJuWF9aK0= github.com/opentdf/platform/lib/ocrypto v0.10.0/go.mod h1:WASkoHreqgTFImB/gJW42VTdpi9AkgkmaW19y/fU+Ew= -github.com/opentdf/platform/protocol/go v0.18.0 h1:7mpdQI4PiU8YQds7TXvmm8Hmj6JVDTiPgwpB5k2pOEg= -github.com/opentdf/platform/protocol/go v0.18.0/go.mod h1:jeDa0o2jce1yELNxZ92hscz0mxxqq7GtqC9EoYCpNW0= +github.com/opentdf/platform/protocol/go v0.19.0 h1:R3+1/doB5S53w4d3ApN2JuapWCXCSK3CYhXXrU6eZ9g= +github.com/opentdf/platform/protocol/go v0.19.0/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/actions.go b/pkg/handlers/actions.go index 701b8ded..7f8db58d 100644 --- a/pkg/handlers/actions.go +++ b/pkg/handlers/actions.go @@ -8,7 +8,7 @@ import ( "github.com/opentdf/platform/protocol/go/policy/actions" ) -func (h Handler) GetAction(ctx context.Context, id string, name string) (*policy.Action, error) { +func (h Handler) GetAction(ctx context.Context, id string, name string, namespace string) (*policy.Action, error) { req := &actions.GetActionRequest{} if id != "" { req.Identifier = &actions.GetActionRequest_Id{ @@ -20,6 +20,8 @@ func (h Handler) GetAction(ctx context.Context, id string, name string) (*policy } } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) + resp, err := h.sdk.Actions.GetAction(ctx, req) if err != nil { return nil, err @@ -28,20 +30,26 @@ func (h Handler) GetAction(ctx context.Context, id string, name string) (*policy return resp.GetAction(), nil } -func (h Handler) ListActions(ctx context.Context, limit, offset int32) (*actions.ListActionsResponse, error) { - return h.sdk.Actions.ListActions(ctx, &actions.ListActionsRequest{ +func (h Handler) ListActions(ctx context.Context, limit, offset int32, namespace string) (*actions.ListActionsResponse, error) { + req := &actions.ListActionsRequest{ Pagination: &policy.PageRequest{ Limit: limit, Offset: offset, }, - }) + } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) + + return h.sdk.Actions.ListActions(ctx, req) } -func (h Handler) CreateAction(ctx context.Context, name string, metadata *common.MetadataMutable) (*policy.Action, error) { - resp, err := h.sdk.Actions.CreateAction(ctx, &actions.CreateActionRequest{ +func (h Handler) CreateAction(ctx context.Context, name string, namespace string, metadata *common.MetadataMutable) (*policy.Action, error) { + req := &actions.CreateActionRequest{ Name: name, Metadata: metadata, - }) + } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) + + resp, err := h.sdk.Actions.CreateAction(ctx, req) if err != nil { return nil, err } @@ -59,7 +67,7 @@ func (h Handler) UpdateAction(ctx context.Context, id, name string, metadata *co if err != nil { return nil, err } - return h.GetAction(ctx, id, "") + return h.GetAction(ctx, id, "", "") } func (h Handler) DeleteAction(ctx context.Context, id string) error { diff --git a/pkg/handlers/namespaces.go b/pkg/handlers/namespaces.go index 4df71f81..6cc2284c 100644 --- a/pkg/handlers/namespaces.go +++ b/pkg/handlers/namespaces.go @@ -11,6 +11,13 @@ import ( "github.com/opentdf/platform/protocol/go/policy/unsafe" ) +func getNamespaceIDAndFQN(namespace string) (string, string) { + if _, err := uuid.Parse(namespace); err != nil { + return "", namespace + } + return namespace, "" +} + func (h Handler) GetNamespace(ctx context.Context, identifier string) (*policy.Namespace, error) { req := &namespaces.GetNamespaceRequest{ Identifier: &namespaces.GetNamespaceRequest_NamespaceId{ diff --git a/pkg/handlers/obligations.go b/pkg/handlers/obligations.go index 5de0d61c..59373ae2 100644 --- a/pkg/handlers/obligations.go +++ b/pkg/handlers/obligations.go @@ -37,13 +37,7 @@ func (h Handler) CreateObligation(ctx context.Context, namespace, name string, v Values: values, Metadata: metadata, } - - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) resp, err := h.sdk.Obligations.CreateObligation(ctx, req) if err != nil { @@ -77,12 +71,7 @@ func (h Handler) ListObligations(ctx context.Context, limit, offset int32, names }, } if namespace != "" { - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) } return h.sdk.Obligations.ListObligations(ctx, req) } @@ -237,12 +226,7 @@ func (h Handler) ListObligationTriggers(ctx context.Context, namespace string, l } if namespace != "" { - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) } return h.sdk.Obligations.ListObligationTriggers(ctx, req) diff --git a/pkg/handlers/registeredResources.go b/pkg/handlers/registeredResources.go index 19b65a1f..cbbcc74e 100644 --- a/pkg/handlers/registeredResources.go +++ b/pkg/handlers/registeredResources.go @@ -3,7 +3,6 @@ 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/registeredresources" @@ -20,12 +19,7 @@ func (h Handler) CreateRegisteredResource(ctx context.Context, namespace, name s Metadata: metadata, } - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) resp, err := h.sdk.RegisteredResources.CreateRegisteredResource(ctx, req) if err != nil { @@ -47,12 +41,7 @@ func (h Handler) GetRegisteredResource(ctx context.Context, id, name, namespace } } if namespace != "" { - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) } resp, err := h.sdk.RegisteredResources.GetRegisteredResource(ctx, req) @@ -71,12 +60,7 @@ func (h Handler) ListRegisteredResources(ctx context.Context, limit, offset int3 }, } if namespace != "" { - _, err := uuid.Parse(namespace) - if err != nil { - req.NamespaceFqn = namespace - } else { - req.NamespaceId = namespace - } + req.NamespaceId, req.NamespaceFqn = getNamespaceIDAndFQN(namespace) } return h.sdk.RegisteredResources.ListRegisteredResources(ctx, req) }