8000 Secret masking in output_schema feature is added by mahesh-orch · Pull Request #5250 · StackStorm/st2 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Secret masking in output_schema feature is added #5250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7589139
Secret masking in output_schema feature is added
shivani-orch Apr 29, 2021
4495271
Update test_util_output_schema.py
mahesh-orch Apr 29, 2021
475d631
Update output_schema.py
mahesh-orch May 10, 2021
335d993
Update test_util_output_schema.py
mahesh-orch May 10, 2021
bc7eaeb
Merge branch 'StackStorm:master' into secret_masking
mahesh-orch May 10, 2021
78fad6b
Update execution.py
mahesh-orch May 10, 2021
b4ae25a
Merge branch 'StackStorm:master' into secret_masking
mahesh-orch May 12, 2021
2720762
Moving logic to /st2common/models/db/execution.py
mahesh-orch May 12, 2021
4511f05
added code for masking output schema secret params
mahesh-orch May 12, 2021
1e38f98
adding a unit test and updating existing tests
mahesh-orch May 12, 2021
8f8822a
Update st2common/st2common/models/api/execution.py
mahesh-orch May 12, 2021
a97f531
Update st2common/st2common/models/api/execution.py
mahesh-orch May 12, 2021
f65c27d
Adding blank lines in st2common/st2common/models/api/execution.py
mahesh-orch May 12, 2021
b220ae4
Updating /st2common/tests/unit/test_db_execution.py
mahesh-orch May 12, 2021
86ae3cf
Add unit tests to cover masking of output for various action runners
m4dcoder May 15, 2021
38b46a5
Minor clean up to codes added for troubleshooting
m4dcoder May 15, 2021
71ff4bf
shifted output schema secret masking block
mahesh-orch May 15, 2021
adc1afd
Moved output masking block to output_schema.py
mahesh-orch May 15, 2021
4ef3765
added unit tests for mask secret output
mahesh-orch May 15, 2021
67461d9
adding new file
mahesh-orch May 15, 2021
2006d26
adding new file
mahesh-orch May 15, 2021
d9a1f84
updating my_action.yaml
mahesh-orch May 15, 2021
83d3214
updating test_output_schema.py
mahesh-orch May 15, 2021
217b5b2
updating dummy_pack_1/my_action.py
mahesh-orch May 15, 2021
fd4399c
updating output_schema.py for using output_key
mahesh-orch May 17, 2021
3a97ad1
Refactor mask_secret_output function to be more robust
m4dcoder May 17, 2021
241cdca
Minor fix to test on number of actions
m4dcoder May 17, 2021
0f3b105
Merge remote-tracking branch 'origin' into secret_masking
m4dcoder May 17, 2021
66afa34
adding a workflow unit test for ouptut schema
mahesh-orch May 18, 2021
5f99494
adding python action for workflow test
mahesh-orch May 18, 2021
399a8ab
adding workflow for unit test for output schema
mahesh-orch May 18, 2021
ab92762
adding action with output schema
mahesh-orch May 18, 2021
46f11c6
adding python action with output schema
mahesh-orch May 18, 2021
add83a0
updating test_data_flow.py
mahesh-orch May 18, 2021
7739f23
updating test_data_flow.py
mahesh-orch May 19, 2021
024b038
adding unit tests for GET API for output schema
mahesh-orch May 20, 2021
595df23
Refactor the test case for testing secret output in task of workflow
m4dcoder May 20, 2021
674f3c3
Minor fix to output schema unit test due to changes in a workflow
m4dcoder May 20, 2021
914a566
Add entry to CHANGELOG for masking of secrets in execution output
m4dcoder May 20, 2021
386b252
Removed unused test fixtures
m4dcoder May 20, 2021
b8402b4
Fix masking of secret output for workflows
m4dcoder May 20, 2021
dc65601
Minor fix to show_secrets param in get_one of output controller
m4dcoder May 20, 2021
575eae9
Add PR # to the changelog entry
m4dcoder May 20, 2021
5c2fe79
Merge remote-tracking branch 'origin' into secret_masking
m4dcoder May 20, 2021
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
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ Added

Contributed by @Kami.

* Mask secrets in output of an action execution in the API if the action has an output schema
defined and one or more output parameters are marked as secret. #5250

Contributed by @mahesh-orch.

Changed
~~~~~~~

Expand Down
78 changes: 73 additions & 5 deletions contrib/runners/orquesta_runner/tests/unit/test_data_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@

tests_config.parse_args()

from python_runner import python_runner
from tests.unit import base

from st2common.bootstrap import actionsregistrar
from st2common.bootstrap import runnersregistrar
from st2common.constants import action as ac_const
from st2common.constants import secrets as secrets_const
from st2common.models.api import execution as ex_api_models
from st2common.models.db import liveaction as lv_db_models
from st2common.persistence import execution as ex_db_access
from st2common.persistence import liveaction as lv_db_access
Expand All @@ -58,6 +61,23 @@
st2tests.fixturesloader.get_fixtures_packs_base_path() + "/core",
]

TEST_1 = "xyz"
TEST_2 = "床前明月光 疑是地上霜 舉頭望明月 低頭思故鄉"
MOCK_PY_RESULT_1 = {
"stderr": "",
"stdout": "",
"result": {"k2": TEST_1},
"exit_code": 0,
}
MOCK_PY_RESULT_2 = {
"stderr": "",
"stdout": "",
"result": {"k2": TEST_2},
"exit_code": 0,
}
MOCK_PY_OUTPUT_1 = (ac_const.LIVEACTION_STATUS_SUCCEEDED, MOCK_PY_RESULT_1, None)
MOCK_PY_OUTPUT_2 = (ac_const.LIVEACTION_STATUS_SUCCEEDED, MOCK_PY_RESULT_2, None)


@mock.patch.object(
publishers.CUDPublisher, "publish_update", mock.MagicMock(return_value=None)
Expand Down Expand Up @@ -172,20 +192,42 @@ def assert_data_flow(self, data):
# Manually handle action execution completion.
wf_svc.handle_action_execution_completion(tk3_ac_ex_db)

# Assert task3 succeeded and workflow is completed.
# Assert task3 succeeded and workflow is still running.
tk3_ex_db = wf_db_access.TaskExecution.get_by_id(tk3_ex_db.id)
self.assertEqual(tk3_ex_db.status, wf_statuses.SUCCEEDED)
wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id)
self.assertEqual(wf_ex_db.status, wf_statuses.RUNNING)

# Assert task4 is already completed.
query_filters = {"workflow_execution": str(wf_ex_db.id), "task_id": "task4"}
tk4_ex_db = wf_db_access.TaskExecution.query(**query_filters)[0]
tk4_ac_ex_db = ex_db_access.ActionExecution.query(
task_execution=str(tk4_ex_db.id)
)[0]
tk4_lv_ac_db = lv_db_access.LiveAction.get_by_id(tk4_ac_ex_db.liveaction["id"])
self.assertEqual(tk4_lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED)

# Manually handle action execution completion.
wf_svc.handle_action_execution_completion(tk4_ac_ex_db)

# Assert task4 succeeded and workflow is completed.
tk4_ex_db = wf_db_access.TaskExecution.get_by_id(tk4_ex_db.id)
self.assertEqual(tk4_ex_db.status, wf_statuses.SUCCEEDED)
wf_ex_db = wf_db_access.WorkflowExecution.get_by_id(wf_ex_db.id)
self.assertEqual(wf_ex_db.status, wf_statuses.SUCCEEDED)
lv_ac_db = lv_db_access.LiveAction.get_by_id(str(lv_ac_db.id))
self.assertEqual(lv_ac_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED)
ac_ex_db = ex_db_access.ActionExecution.get_by_id(str(ac_ex_db.id))
self.assertEqual(ac_ex_db.status, ac_const.LIVEACTION_STATUS_SUCCEEDED)

# Check workflow output.
expected_value = wf_input["a1"] if six.PY3 else wf_input["a1"].decode("utf-8")

expected_output = {
"a5": wf_input["a1"] if six.PY3 else wf_input["a1"].decode("utf-8"),
"b5": wf_input["a1"] if six.PY3 else wf_input["a1"].decode("utf-8"),
"a6": expected_value,
"b6": expected_value,
"a7": expected_value,
"b7": expected_value,
}

self.assertDictEqual(wf_ex_db.output, expected_output)
Expand All @@ -196,8 +238,34 @@ def assert_data_flow(self, data):
self.assertDictEqual(lv_ac_db.result, expected_result)
self.assertDictEqual(ac_ex_db.result, expected_result)

# Assert expected output on conversion to API model
ac_ex_api = ex_api_models.ActionExecutionAPI.from_model(
ac_ex_db, mask_secrets=True
)

expected_masked_output = {
"a6": expected_value,
"b6": expected_value,
"a7": expected_value,
"b7": secrets_const.MASKED_ATTRIBUTE_VALUE,
}

expected_masked_result = {"output": expected_masked_output}

self.assertDictEqual(ac_ex_api.result, expected_masked_result)

@mock.patch.object(
python_runner.PythonRunner,
"run",
mock.MagicMock(return_value=MOCK_PY_OUTPUT_1),
)
def test_string(self):
self.assert_data_flow("xyz")
self.assert_data_flow(TEST_1)

@mock.patch.object(
python_runner.PythonRunner,
"run",
mock.MagicMock(return_value=MOCK_PY_OUTPUT_2),
)
def test_unicode_string(self):
self.assert_data_flow("床前明月光 疑是地上霜 舉頭望明月 低頭思故鄉")
self.assert_data_flow(TEST_2)
201 changes: 201 additions & 0 deletions st2actions/tests/unit/test_output_schema.py
EA0F
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Copyright 2021 The StackStorm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import

import mock

from http_runner import http_runner
from python_runner import python_runner
from orquesta_runner import orquesta_runner

import st2tests

import st2tests.config as tests_config

tests_config.parse_args()

from st2common.bootstrap import actionsregistrar
from st2common.bootstrap import runnersregistrar
from st2common.constants import action as ac_const
from st2common.constants import secrets as secrets_const
from st2common.models.api import execution as ex_api_models
from st2common.models.db import liveaction as lv_db_models
from st2common.services import action as action_service
from st2common.transport import liveaction as lv_ac_xport
from st2common.transport import publishers
from st2tests.mocks import liveaction as mock_lv_ac_xport


PACKS = [
st2tests.fixturesloader.get_fixtures_packs_base_path() + "/dummy_pack_1",
st2tests.fixturesloader.get_fixtures_packs_base_path() + "/orquesta_tests",
]

MOCK_PYTHON_ACTION_RESULT = {
"stderr": "",
"stdout": "",
"result": {"k1": "foobar", "k2": "shhhh!"},
"exit_code": 0,
}

MOCK_PYTHON_RUNNER_OUTPUT = (
ac_const.LIVEACTION_STATUS_SUCCEEDED,
MOCK_PYTHON_ACTION_RESULT,
None,
)

MOCK_HTTP_ACTION_RESULT = {
"status_code": 200,
"body": {"k1": "foobar", "k2": "shhhh!"},
}

MOCK_HTTP_RUNNER_OUTPUT = (
ac_const.LIVEACTION_STATUS_SUCCEEDED,
MOCK_HTTP_ACTION_RESULT,
None,
)

MOCK_ORQUESTA_ACTION_RESULT = {
"errors": [],
"output": {"a6": "foobar", "b6": "foobar", "a7": "foobar", "b7": "shhhh!"},
}

MOCK_ORQUESTA_RUNNER_OUTPUT = (
ac_const.LIVEACTION_STATUS_SUCCEEDED,
MOCK_ORQUESTA_ACTION_RESULT,
None,
)


@mock.patch.object(
publishers.CUDPublisher, "publish_update", mock.MagicMock(return_value=None)
)
@mock.patch.object(
publishers.CUDPublisher,
"publish_create",
mock.MagicMock(side_effect=mock_lv_ac_xport.MockLiveActionPublisher.publish_create),
)
@mock.patch.object(
lv_ac_xport.LiveActionPublisher,
"publish_state",
mock.MagicMock(side_effect=mock_lv_ac_xport.MockLiveActionPublisher.publish_state),
)
class ActionExecutionOutputSchemaTest(st2tests.ExecutionDbTestCase):
@classmethod
def setUpClass(cls):
super(ActionExecutionOutputSchemaTest, cls).setUpClass()

# Register runners.
runnersregistrar.register_runners()

# Register test pack(s).
actions_registrar = actionsregistrar.ActionsRegistrar(
use_pack_cache=False, fail_on_failure=True
)

for pack in PACKS:
actions_registrar.register_from_pack(pack)

@mock.patch.object(
python_runner.PythonRunner,
"run",
mock.MagicMock(return_value=MOCK_PYTHON_RUNNER_OUTPUT),
)
def test_python_action(self):
# Execute a python action with output schema and secret
lv_ac_db = lv_db_models.LiveActionDB(action="dummy_pack_1.my_py_action")
lv_ac_db, ac_ex_db = action_service.request(lv_ac_db)
ac_ex_db = self._wait_on_ac_ex_status(
ac_ex_db, ac_const.LIVEACTION_STATUS_SUCCEEDED
)

# Assert expected output written to the database
expected_output = {"k1": "foobar", "k2": "shhhh!"}
self.assertDictEqual(ac_ex_db.result["result"], expected_output)

# Assert expected output on conversion to API model
ac_ex_api = ex_api_models.ActionExecutionAPI.from_model(
ac_ex_db, mask_secrets=True
)
expected_masked_output = {
"k1": "foobar",
"k2": secrets_const.MASKED_ATTRIBUTE_VALUE,
}
self.assertDictEqual(ac_ex_api.result["result"], expected_masked_output)

@mock.patch.object(
http_runner.HttpRunner,
"run",
mock.MagicMock(return_value=MOCK_HTTP_RUNNER_OUTPUT),
)
def test_http_action(self):
# Execute a http action with output schema and secret
lv_ac_db = lv_db_models.LiveActionDB(action="dummy_pack_1.my_http_action")
lv_ac_db, ac_ex_db = action_service.request(lv_ac_db)
ac_ex_db = self._wait_on_ac_ex_status(
ac_ex_db, ac_const.LIVEACTION_STATUS_SUCCEEDED
)

# Assert expected output written to the database
expected_output = {"k1": "foobar", "k2": "shhhh!"}
self.assertDictEqual(ac_ex_db.result["body"], expected_output)

# Assert expected output on conversion to API model
ac_ex_api = ex_api_models.ActionExecutionAPI.from_model(
ac_ex_db, mask_secrets=True
)
expected_masked_output = {
"k1": "foobar",
"k2": secrets_const.MASKED_ATTRIBUTE_VALUE,
}
self.assertDictEqual(ac_ex_api.result["body"], expected_masked_output)

@mock.patch.object(
orquesta_runner.OrquestaRunner,
"run",
mock.MagicMock(return_value=MOCK_ORQUESTA_RUNNER_OUTPUT),
)
def test_orquesta_action(self):
wf_input = "foobar"

# Execute an orquesta action with output schema and secret
lv_ac_db = lv_db_models.LiveActionDB(
action="orquesta_tests.data-flow", parameters={"a1": wf_input}
)
lv_ac_db, ac_ex_db = action_service.request(lv_ac_db)
ac_ex_db = self._wait_on_ac_ex_status(
ac_ex_db, ac_const.LIVEACTION_STATUS_SUCCEEDED
)

# Assert expected output written to the database
expected_output = {
"a6": wf_input,
"b6": wf_input,
"a7": wf_input,
"b7": "shhhh!",
}
self.assertDictEqual(ac_ex_db.result["output"], expected_output)

# Assert expected output on conversion to API model
ac_ex_api = ex_api_models.ActionExecutionAPI.from_model(
ac_ex_db, mask_secrets=True
)
expected_masked_output = {
"a6": wf_input,
"b6": wf_input,
"a7": wf_input,
"b7": secrets_const.MASKED_ATTRIBUTE_VALUE,
}
self.assertDictEqual(ac_ex_api.result["output"], expected_masked_output)
Loading
0