From 5843f1d7655ae55e476732559aeba20c89457fd7 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 14 Feb 2024 11:45:04 -0500 Subject: [PATCH 01/14] add message redaction -Add code to redact messages that may contain additionally redacted secrets based on the `racf_key`. -Update unit testing to account for this Signed-off-by: Elijah Swift --- pyracf/common/utilities/logger.py | 14 ++++++++++++++ .../alter_user_additional_secret_added_error.log | 12 ++++++------ .../alter_user_result_error_uid_secret.json | 6 +++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index abe85099..668cecb5 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -224,6 +224,20 @@ def redact_result_xml( security_result = self.__redact_string( security_result, match.end(), end_pattern ) + if isinstance(security_result, bytes): + security_result = re.sub( + rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", + rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " + + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION ", + security_result.decode("cp1047"), + ).encode("cp1047") + else: + security_result = re.sub( + rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", + rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " + + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION ", + security_result, + ) return security_result def __colorize_json(self, json_text: str) -> str: diff --git a/tests/user/user_log_samples/alter_user_additional_secret_added_error.log b/tests/user/user_log_samples/alter_user_additional_secret_added_error.log index 2e02e6c4..73f258ed 100644 --- a/tests/user/user_log_samples/alter_user_additional_secret_added_error.log +++ b/tests/user/user_log_samples/alter_user_additional_secret_added_error.log @@ -240,9 +240,9 @@ 16 8 ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********)) - IKJ56702I INVALID UID, 90000000000 - IKJ56701I MISSING OMVS UID+ - IKJ56701I MISSING OMVS USER ID (UID), 1-10 NUMERIC DIGITS + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION 4 @@ -271,9 +271,9 @@ "reasonCode": 8, "image": "ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********))", "messages": [ - "IKJ56702I INVALID UID, 90000000000", - "IKJ56701I MISSING OMVS UID+", - "IKJ56701I MISSING OMVS USER ID (UID), 1-10 NUMERIC DIGITS" + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION ", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION ", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION " ] } ] diff --git a/tests/user/user_result_samples/alter_user_result_error_uid_secret.json b/tests/user/user_result_samples/alter_user_result_error_uid_secret.json index 7bfd2100..c131ea5a 100644 --- a/tests/user/user_result_samples/alter_user_result_error_uid_secret.json +++ b/tests/user/user_result_samples/alter_user_result_error_uid_secret.json @@ -14,9 +14,9 @@ "reasonCode": 8, "image": "ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********))", "messages": [ - "IKJ56702I INVALID UID, 90000000000", - "IKJ56701I MISSING OMVS UID+", - "IKJ56701I MISSING OMVS USER ID (UID), 1-10 NUMERIC DIGITS" + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION ", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION ", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION " ] } ] From 945bf9218e4840ff0c55b8ae6352e61b9de681e4 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 14 Feb 2024 11:46:16 -0500 Subject: [PATCH 02/14] Clean up message redaction string Signed-off-by: Elijah Swift --- pyracf/common/utilities/logger.py | 4 ++-- .../alter_user_additional_secret_added_error.log | 12 ++++++------ .../alter_user_result_error_uid_secret.json | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 668cecb5..ed379d53 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -228,14 +228,14 @@ def redact_result_xml( security_result = re.sub( rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " - + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION ", + + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION", security_result.decode("cp1047"), ).encode("cp1047") else: security_result = re.sub( rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " - + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION ", + + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION", security_result, ) return security_result diff --git a/tests/user/user_log_samples/alter_user_additional_secret_added_error.log b/tests/user/user_log_samples/alter_user_additional_secret_added_error.log index 73f258ed..78e2dbb6 100644 --- a/tests/user/user_log_samples/alter_user_additional_secret_added_error.log +++ b/tests/user/user_log_samples/alter_user_additional_secret_added_error.log @@ -240,9 +240,9 @@ 16 8 ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********)) - REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION - REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION - REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION 4 @@ -271,9 +271,9 @@ "reasonCode": 8, "image": "ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********))", "messages": [ - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION ", - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION ", - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION " + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION" ] } ] diff --git a/tests/user/user_result_samples/alter_user_result_error_uid_secret.json b/tests/user/user_result_samples/alter_user_result_error_uid_secret.json index c131ea5a..65939e45 100644 --- a/tests/user/user_result_samples/alter_user_result_error_uid_secret.json +++ b/tests/user/user_result_samples/alter_user_result_error_uid_secret.json @@ -14,9 +14,9 @@ "reasonCode": 8, "image": "ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM UID (********))", "messages": [ - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION ", - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION ", - "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION " + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION" ] } ] From 9d1e0cbd31e3f71860d1990bffc8cce592c985ea Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 14 Feb 2024 16:37:42 -0500 Subject: [PATCH 03/14] Update logger.py -Update redaction logic for request and result xml data. -Check for Value () or Value ('') (the single quotes are for full strings that can contain "delimiters" like () otherwise this would mess with IKJPARSE. This works well for our logic too) patterns and use non-greedy regular expressions to replace the values specified in the patterns -This passes all existing unit tests. New unit tests will be built for possible commands not seen (would have failed on previous logic) Signed-off-by: Elijah Swift --- pyracf/common/utilities/logger.py | 45 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index ed379d53..4449e69b 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -149,19 +149,33 @@ def __redact_request_dictionary( def __redact_string( self, input_string: Union[str, bytes], - start_ind: int, - end_pattern: Union[str, bytes], - ): + key: str, + ) -> Union[str, bytes]: """ - Redacts characters in a string between a starting index and ending pattern. + Redacts characters in a string between a starting index and ending tag. Replaces the identified characters with '********' regardless of the original length. """ asterisks = "********" + is_bytes = False if isinstance(input_string, bytes): - asterisks = asterisks.encode("cp1047") - pre_keyword = input_string[:start_ind] - post_keyword = end_pattern.join(input_string[start_ind:].split(end_pattern)[1:]) - return pre_keyword + asterisks + end_pattern + post_keyword + input_string = input_string.decode("cp1047") + is_bytes = True + quoted = re.search(rf"{key.upper()}( +)\(\'.*?(? if f"" not in xml_string: continue - xml_string = self.__redact_string(xml_string, match.end(), f".*<\/{xml_key}", + rf"{xml_key}\1>******** str: """ Redacts a list of specific secret traits in a result xml string. - Based on the following RACF command pattern: + Based on the following RACF command patterns: 'TRAIT (value)' + "TRAIT ('value')" This function also accounts for varied amounts of whitespace in the pattern. """ if isinstance(security_result, list): return security_result for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - end_pattern = ")" if isinstance(security_result, bytes): match = re.search( rf"{racf_key.upper()} +\(", security_result.decode("cp1047") ) - end_pattern = end_pattern.encode("cp1047") else: match = re.search(rf"{racf_key.upper()} +\(", security_result) if not match: continue - security_result = self.__redact_string( - security_result, match.end(), end_pattern - ) + security_result = self.__redact_string(security_result, racf_key) if isinstance(security_result, bytes): security_result = re.sub( rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", From 1ea6dd44ed96a7f6e29c8d4ba4993fb12b5ddf36 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 14 Feb 2024 16:59:04 -0500 Subject: [PATCH 04/14] Added a unit test to verify new functions work Added a unit test to verify installation data can be redacted when contains characters like () (on requests) Signed-off-by: Elijah Swift --- pyracf/common/utilities/logger.py | 4 ++-- tests/user/test_user_constants.py | 10 ++++++++ tests/user/test_user_result_parser.py | 24 +++++++++++++++++++ .../alter_user_result_success_inst_data.xml | 14 +++++++++++ ..._user_result_success_inst_data_secret.json | 23 ++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 tests/user/user_result_samples/alter_user_result_success_inst_data.xml create mode 100644 tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 4449e69b..e27f70be 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -160,10 +160,10 @@ def __redact_string( if isinstance(input_string, bytes): input_string = input_string.decode("cp1047") is_bytes = True - quoted = re.search(rf"{key.upper()}( +)\(\'.*?(? Union[str, bytes]: TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY = get_sample( "alter_user_result_error_uid_secret.json" ) +TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML = get_sample( + "alter_user_result_success_inst_data.xml" +) +TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY = get_sample( + "alter_user_result_success_inst_data_secret.json" +) # Extract User TEST_EXTRACT_USER_RESULT_BASE_OMVS_SUCCESS_XML = get_sample( @@ -218,6 +224,10 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR = dict(TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED) TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"] = 90000000000 +TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA = dict(TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED) +TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA["base:installation_data"] = ( + "Test = Value; Other(stuff goes here)'')" +) # Extract User TEST_EXTRACT_USER_REQUEST_BASE_OMVS_XML = get_sample( diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index ad458f23..cc9177f3 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -523,3 +523,27 @@ def test_user_admin_custom_secret_redacted_on_error( f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", exception.exception.result, ) + + def test_user_admin_custom_secret_redacted_when_complex_characters( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(additional_secret_traits=["base:installation_data"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, + ] + result = user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, + ) + self.assertEqual( + result, + TestUserConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ + "base:installation_data" + ], + result, + ) diff --git a/tests/user/user_result_samples/alter_user_result_success_inst_data.xml b/tests/user/user_result_samples/alter_user_result_success_inst_data.xml new file mode 100644 index 00000000..341d1ff6 --- /dev/null +++ b/tests/user/user_result_samples/alter_user_result_success_inst_data.xml @@ -0,0 +1,14 @@ + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTUSER SQUIDWRD NAME ('Squidward') OWNER (leonard) SPECIAL DATA ('Test = Value; Other(stuff goes here)'')') OMVS (UID (2424) HOME ('/u/squidwrd') PROGRAM ('/bin/sh')) + + + 0 + 0 + \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json b/tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json new file mode 100644 index 00000000..bed7176d --- /dev/null +++ b/tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json @@ -0,0 +1,23 @@ +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "set", + "requestId": "UserRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTUSER SQUIDWRD NAME ('Squidward') OWNER (leonard) SPECIAL DATA ('********') OMVS (UID (2424) HOME ('/u/squidwrd') PROGRAM ('/bin/sh'))" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file From 354e407adf3cb3077a0033165a24ea08bcac78b2 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 20 Feb 2024 10:54:43 -0500 Subject: [PATCH 05/14] Update pyproject.toml Signed-off-by: Elijah Swift --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 46884260..a2fbc483 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [tool.poetry] name="pyracf" - version="1.0b5" + version="1.0b6" description="Python interface to RACF using IRRSMO00 RACF Callable Service." license = "Apache-2.0" authors = [ From e796e094f37f32d4623a77fa232b92f933919c12 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 21 Feb 2024 13:12:46 -0500 Subject: [PATCH 06/14] Move Secrets Redaction Unit Testing To Common -Move Secrets Redaction Unit Testing To Common -Generalize Secrets Redaction Unit testing to other applicable function groups -Add mapping in logger functions for RACF Keys that do not match values in the trait dictionary -Add mapping in logger functions for RACF Message keys that do not match the value in the RACF key Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 3 + pyracf/common/utilities/logger.py | 14 + .../test_additional_secrets_redaction.py | 248 ++++++++++++++++++ ...ter_data_set_result_error_uacc_secret.json | 33 +++ ..._data_set_result_success_owner_secret.json | 24 ++ tests/data_set/test_data_set_constants.py | 6 + .../alter_group_result_error_gid_secret.json | 28 ++ ...alter_group_result_success_gid_secret.json | 23 ++ tests/group/test_group_constants.py | 6 + ...ter_resource_result_error_uacc_secret.json | 29 ++ ..._resource_result_success_owner_secret.json | 27 ++ tests/resource/test_resource_constants.py | 6 + tests/test_runner.py | 4 + tests/user/test_user_result_parser.py | 74 ------ 14 files changed, 451 insertions(+), 74 deletions(-) create mode 100644 tests/common/test_additional_secrets_redaction.py create mode 100644 tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json create mode 100644 tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json create mode 100644 tests/group/group_result_samples/alter_group_result_error_gid_secret.json create mode 100644 tests/group/group_result_samples/alter_group_result_success_gid_secret.json create mode 100644 tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json create mode 100644 tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index d916b1fe..cc55ea5d 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -177,6 +177,9 @@ def __raw_dump(self) -> None: # ============================================================================ def __add_additional_secret_traits(self, additional_secret_traits: list) -> None: """Add additional fields to be redacted in logger output.""" + unsupported_profile_types = ["permission", "groupConnection", "systemSettings"] + if self._profile_type in unsupported_profile_types: + return for secret in additional_secret_traits: if secret in self.__secret_traits: continue diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index e27f70be..05c69e9d 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -11,6 +11,16 @@ class Logger: """Logging for pyRACF.""" + __racf_key_map = { + "audaltr": "audit", + "audcntl": "audit", + "audnone": "audit", + "audread": "audit", + "audupdt": "audit", + } + + __racf_message_key_map = {"uacc": "universal access"} + __ansi_reset = "\033[0m" __ansi_gray = "\033[2m" __ansi_green = "\033[32m" @@ -230,6 +240,8 @@ def redact_result_xml( return security_result for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key + if racf_key in self.__racf_key_map: + racf_key = self.__racf_key_map[racf_key] if isinstance(security_result, bytes): match = re.search( rf"{racf_key.upper()} +\(", security_result.decode("cp1047") @@ -239,6 +251,8 @@ def redact_result_xml( if not match: continue security_result = self.__redact_string(security_result, racf_key) + if racf_key in self.__racf_message_key_map: + racf_key = self.__racf_message_key_map[racf_key] if isinstance(security_result, bytes): security_result = re.sub( rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py new file mode 100644 index 00000000..5edca9bf --- /dev/null +++ b/tests/common/test_additional_secrets_redaction.py @@ -0,0 +1,248 @@ +"""Test customizing security admin segment traits.""" + +import unittest +from unittest.mock import Mock, patch + +import __init__ + +import tests.data_set.test_data_set_constants as TestDataSetConstants +import tests.group.test_group_constants as TestGroupConstants +import tests.resource.test_resource_constants as TestResourceConstants +import tests.user.test_user_constants as TestUserConstants +from pyracf import ( + DataSetAdmin, + GroupAdmin, + ResourceAdmin, + SecurityRequestError, + UserAdmin, +) + +# Resolves F401 +__init__ + + +@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") +class TestAdditionalSecretsRedaction(unittest.TestCase): + maxDiff = None + + # ============================================================================ + # User Administration + # ============================================================================ + def test_user_admin_custom_secret_redacted_on_success( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, + ] + result = user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, + ) + self.assertEqual( + result, + TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_DICTIONARY, + ) + self.assertNotIn( + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + result, + ) + + def test_user_admin_custom_secret_redacted_on_error( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, + ) + self.assertEqual( + exception.exception.result, + TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"], + exception.exception.result, + ) + + def test_user_admin_custom_secret_redacted_when_complex_characters( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(additional_secret_traits=["base:installation_data"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, + ] + result = user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, + ) + self.assertEqual( + result, + TestUserConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ + "base:installation_data" + ], + result, + ) + + # ============================================================================ + # Group Administration + # ============================================================================ + def test_group_admin_custom_secret_redacted_on_success( + self, + call_racf_mock: Mock, + ): + group_admin = GroupAdmin(additional_secret_traits=["omvs:gid"]) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_XML, + ] + result = group_admin.alter( + "testgrp0", + traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS, + ) + self.assertEqual( + result, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], + result, + ) + + def test_group_admin_custom_secret_redacted_on_error( + self, + call_racf_mock: Mock, + ): + group_admin = GroupAdmin(additional_secret_traits=["omvs:gid"]) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + group_admin.alter( + "testgrp0", + traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, + ) + self.assertEqual( + exception.exception.result, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"], + exception.exception.result, + ) + + # ============================================================================ + # General Resource Profile Administration + # ============================================================================ + def test_resource_admin_custom_secret_redacted_on_success( + self, + call_racf_mock: Mock, + ): + resource_admin = ResourceAdmin(additional_secret_traits=["base:owner"]) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_XML, + ] + result = resource_admin.alter( + "TESTING", + "ELIJTEST", + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS, + ) + self.assertEqual( + result, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS["base:owner"], + result, + ) + + def test_resource_admin_custom_secret_redacted_on_error( + self, + call_racf_mock: Mock, + ): + secret_trait = "base:universal_access" + resource_admin = ResourceAdmin(additional_secret_traits=[secret_trait]) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + resource_admin.alter( + "TESTING", + "ELIJTEST", + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS, + ) + self.assertEqual( + exception.exception.result, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS[ + f"{secret_trait}" + ], + exception.exception.result, + ) + + # ============================================================================ + # Data Set Profile Administration + # ============================================================================ + def test_data_set_admin_custom_secret_redacted_on_success( + self, + call_racf_mock: Mock, + ): + data_set_admin = DataSetAdmin(additional_secret_traits=["base:owner"]) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_XML, + ] + result = data_set_admin.alter( + "ESWIFT.TEST.T1136242.P3020470", + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, + ) + self.assertEqual( + result, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS["base:owner"], + result, + ) + + def test_data_set_admin_custom_secret_redacted_on_error( + self, + call_racf_mock: Mock, + ): + secret_trait = "base:universal_access" + data_set_admin = DataSetAdmin(additional_secret_traits=[secret_trait]) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + data_set_admin.alter( + "ESWIFT.TEST.T1136242.P3020470", + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, + ) + self.assertEqual( + exception.exception.result, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY, + ) + self.assertNotIn( + TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS[f"{secret_trait}"], + exception.exception.result, + ) diff --git a/tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json b/tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json new file mode 100644 index 00000000..cdf46ee5 --- /dev/null +++ b/tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json @@ -0,0 +1,33 @@ +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T113622.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ADDSD ('ESWIFT.TEST.T113622.P3020470')", + "messages": [ + "ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG" + ] + }, + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (********)", + "messages": [ + "ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json b/tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json new file mode 100644 index 00000000..3d93043a --- /dev/null +++ b/tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json @@ -0,0 +1,24 @@ +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (********)" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/data_set/test_data_set_constants.py b/tests/data_set/test_data_set_constants.py index cf050ab2..22af3969 100644 --- a/tests/data_set/test_data_set_constants.py +++ b/tests/data_set/test_data_set_constants.py @@ -34,10 +34,16 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_DATA_SET_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_data_set_result_success.json" ) +TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( + "alter_data_set_result_success_owner_secret.json" +) TEST_ALTER_DATA_SET_RESULT_ERROR_XML = get_sample("alter_data_set_result_error.xml") TEST_ALTER_DATA_SET_RESULT_ERROR_DICTIONARY = get_sample( "alter_data_set_result_error.json" ) +TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( + "alter_data_set_result_error_uacc_secret.json" +) # Extract Data Set TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML = get_sample( diff --git a/tests/group/group_result_samples/alter_group_result_error_gid_secret.json b/tests/group/group_result_samples/alter_group_result_error_gid_secret.json new file mode 100644 index 00000000..d201e1d8 --- /dev/null +++ b/tests/group/group_result_samples/alter_group_result_error_gid_secret.json @@ -0,0 +1,28 @@ +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "ALTGROUP TESTGRP0 OMVS (GID (********))", + "messages": [ + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/group/group_result_samples/alter_group_result_success_gid_secret.json b/tests/group/group_result_samples/alter_group_result_success_gid_secret.json new file mode 100644 index 00000000..40655569 --- /dev/null +++ b/tests/group/group_result_samples/alter_group_result_success_gid_secret.json @@ -0,0 +1,23 @@ +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTGROUP TESTGRP0 OMVS (GID (********))" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/group/test_group_constants.py b/tests/group/test_group_constants.py index 478a1ca9..5af92c68 100644 --- a/tests/group/test_group_constants.py +++ b/tests/group/test_group_constants.py @@ -26,8 +26,14 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_GROUP_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_group_result_success.json" ) +TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY = get_sample( + "alter_group_result_success_gid_secret.json" +) TEST_ALTER_GROUP_RESULT_ERROR_XML = get_sample("alter_group_result_error.xml") TEST_ALTER_GROUP_RESULT_ERROR_DICTIONARY = get_sample("alter_group_result_error.json") +TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY = get_sample( + "alter_group_result_error_gid_secret.json" +) # Extract Group TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML = get_sample( diff --git a/tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json b/tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json new file mode 100644 index 00000000..45dfdafc --- /dev/null +++ b/tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json @@ -0,0 +1,29 @@ +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "RALTER ELIJTEST (TESTING) UACC (********) OWNER (eswift)", + "messages": [ + "REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "IKJ56701I MISSING ALTER, CONTROL, UPDATE, READ, EXECUTE, OR NONE" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json b/tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json new file mode 100644 index 00000000..bbe64c25 --- /dev/null +++ b/tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json @@ -0,0 +1,27 @@ +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RALTER ELIJTEST (TESTING) UACC (Read) OWNER (********)", + "messages": [ + "ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/resource/test_resource_constants.py b/tests/resource/test_resource_constants.py index a1448ccd..edbdd8ea 100644 --- a/tests/resource/test_resource_constants.py +++ b/tests/resource/test_resource_constants.py @@ -28,10 +28,16 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_RESOURCE_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_resource_result_success.json" ) +TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( + "alter_resource_result_success_owner_secret.json" +) TEST_ALTER_RESOURCE_RESULT_ERROR_XML = get_sample("alter_resource_result_error.xml") TEST_ALTER_RESOURCE_RESULT_ERROR_DICTIONARY = get_sample( "alter_resource_result_error.json" ) +TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( + "alter_resource_result_error_uacc_secret.json" +) # Extract Resource TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML = get_sample( diff --git a/tests/test_runner.py b/tests/test_runner.py index 44a4f573..c1845637 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -10,6 +10,9 @@ from tests.access.test_access_debug_logging import TestAccessDebugLogging from tests.access.test_access_request_builder import TestAccessRequestBuilder from tests.access.test_access_result_parser import TestAccessResultParser +from tests.common.test_additional_secrets_redaction import ( + TestAdditionalSecretsRedaction, +) from tests.common.test_class_attributes import TestClassAttributes from tests.common.test_customize_segment_traits import TestCustomizeSegmentTraits from tests.common.test_downstream_fatal_error import TestDownstreamFatalError @@ -69,6 +72,7 @@ def __test_suite() -> unittest.TestSuite: TestAccessResultParser, TestAccessRequestBuilder, TestAccessDebugLogging, + TestAdditionalSecretsRedaction, TestClassAttributes, TestCustomizeSegmentTraits, TestSetupPrecheck, diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index cc9177f3..d4d8d024 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -473,77 +473,3 @@ def test_user_admin_can_parse_delete_user_error_xml( exception.exception.result, TestUserConstants.TEST_DELETE_USER_RESULT_ERROR_DICTIONARY, ) - - # ============================================================================ - # Add Additional Secrets - # ============================================================================ - def test_user_admin_custom_secret_redacted_on_success( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, - ] - result = user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, - ) - self.assertEqual( - result, - TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_DICTIONARY, - ) - self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], - result, - ) - - # Secret redacted from command image but not from resulting error message. - # Marked experimental until resolved - def test_user_admin_custom_secret_redacted_on_error( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, - ] - with self.assertRaises(SecurityRequestError) as exception: - user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, - ) - self.assertEqual( - exception.exception.result, - TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY, - ) - self.assertNotIn( - f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", - exception.exception.result, - ) - - def test_user_admin_custom_secret_redacted_when_complex_characters( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(additional_secret_traits=["base:installation_data"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, - ] - result = user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, - ) - self.assertEqual( - result, - TestUserConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, - ) - self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ - "base:installation_data" - ], - result, - ) From f25153cf0b02f15c3ae2523b56ee8f179a93a1e7 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 21 Feb 2024 15:46:39 -0500 Subject: [PATCH 07/14] Add Unit Testing for Additional Secrets Redaction -Unit Test for Redacting traits that map based on logger dictionary (Audit rules) -Unit test for traits that have different message strings (UACC) Signed-off-by: Elijah Swift --- pyracf/common/utilities/logger.py | 29 ++++-- .../test_additional_secrets_redaction.py | 98 ++++++++++++++++++- ...esource_overwrite_audit_result_success.xml | 15 +++ ..._resource_result_success_audit_secret.json | 29 ++++++ tests/resource/test_resource_constants.py | 6 ++ 5 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 tests/resource/resource_result_samples/alter_resource_overwrite_audit_result_success.xml create mode 100644 tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 05c69e9d..850e92a9 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -170,19 +170,29 @@ def __redact_string( if isinstance(input_string, bytes): input_string = input_string.decode("cp1047") is_bytes = True - quoted = re.search(rf"{key.upper()}( +)\(\'.*?(? + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + RALTER ELIJTEST (TESTING) AUDIT( ALL (UPDATE )) + ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED. + + + 0 + 0 + \ No newline at end of file diff --git a/tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json b/tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json new file mode 100644 index 00000000..0d456a97 --- /dev/null +++ b/tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json @@ -0,0 +1,29 @@ +{ + "step1": { + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RALTER ELIJTEST (TESTING) AUDIT(********)", + "messages": [ + "ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } + } +} \ No newline at end of file diff --git a/tests/resource/test_resource_constants.py b/tests/resource/test_resource_constants.py index edbdd8ea..2e071c51 100644 --- a/tests/resource/test_resource_constants.py +++ b/tests/resource/test_resource_constants.py @@ -25,12 +25,18 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Alter Resource TEST_ALTER_RESOURCE_RESULT_SUCCESS_XML = get_sample("alter_resource_result_success.xml") +TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML = get_sample( + "alter_resource_overwrite_audit_result_success.xml" +) TEST_ALTER_RESOURCE_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_resource_result_success.json" ) TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( "alter_resource_result_success_owner_secret.json" ) +TEST_ALTER_RESOURCE_RESULT_SUCCESS_AUDIT_SECRET_DICTIONARY = get_sample( + "alter_resource_result_success_audit_secret.json" +) TEST_ALTER_RESOURCE_RESULT_ERROR_XML = get_sample("alter_resource_result_error.xml") TEST_ALTER_RESOURCE_RESULT_ERROR_DICTIONARY = get_sample( "alter_resource_result_error.json" From d96978fcd54686602da1c7aa60b9b45ad482697c Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 21 Feb 2024 16:18:27 -0500 Subject: [PATCH 08/14] Remove Experimental Tag -Remove Experimental Tag for logging of additional secret traits -Update lots of docstrings Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 1 - pyracf/common/utilities/logger.py | 70 ++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index cc55ea5d..b6b807f3 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -119,7 +119,6 @@ def __init__( self._logger.log_experimental("Replace Existing Segment Traits") self.__replace_valid_segment_traits(replace_existing_segment_traits) if additional_secret_traits is not None: - self._logger.log_experimental("Add Additional Secret Traits") self.__add_additional_secret_traits(additional_secret_traits) self.set_running_userid(run_as_userid) diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 850e92a9..b6c48c51 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -159,38 +159,77 @@ def __redact_request_dictionary( def __redact_string( self, input_string: Union[str, bytes], - key: str, + trait_key: str, ) -> Union[str, bytes]: - """ + r""" Redacts characters in a string between a starting index and ending tag. Replaces the identified characters with '********' regardless of the original length. + + This function employs the following regular expressions explained below + + Regex 1 ("quoted") + This is designed to match the pattern TRAIT('value') by matching the TRAIT name: + {trait_key.upper()}, a variable (potentially zero) amount of spaces ( +){{0,}}, then the + ('value') portion which must start and end with (' and '), but can conceivably contain + any characters, but a negative lookbehind is used to look for any unescaped single quotes + .*?(? Union[str, bytes]: - """ + r""" Redact a list of specific secret traits in a request xml string or bytes object. Based the following xml pattern: 'xml value' This function also accounts for any number of arbitrary xml attributes. + + This function employs the following regular expression: + {xml_key}(.*)>.*<\/{xml_key} - Designed to match the above pattern by starting and ending + with the xmltag string as shown, but the starting tage allows for any characters between + "xmltag" and the > character to allow for the attribute specification shown above. This + results in the starting of the xml as {xml_key}(.*)> and the ending as <\/{xml_key}. + The miscellaneous characters are "captured" as a variable and preserved by the + substitution operation through the use of the \1 supplied in the replacement string. + Between these tags, any non-newline characters are allowed using the .* expression. """ is_bytes = False if isinstance(xml_string, bytes): From ff531624436b58d8881c3b67689a7ea056e702e2 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 28 Feb 2024 17:18:54 -0500 Subject: [PATCH 09/14] Reorginazation -Add back experimental tag (likely true at release) -Trim down Regex comments -Rearchitect racf_key_map and racf_message_key_map to belong to individual administration objects -Move Unit test samples used ONLY for additional secrets testing to Common Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 14 +++- pyracf/common/utilities/logger.py | 76 +++++++++---------- pyracf/data_set/data_set_admin.py | 8 ++ pyracf/resource/resource_admin.py | 8 ++ ...ter_data_set_result_error_uacc_secret.json | 0 ..._data_set_result_success_owner_secret.json | 0 .../alter_group_result_error_gid_secret.json | 0 ...alter_group_result_success_gid_secret.json | 0 ...esource_overwrite_audit_result_success.xml | 0 ...ter_resource_result_error_uacc_secret.json | 0 ..._resource_result_success_audit_secret.json | 0 ..._resource_result_success_owner_secret.json | 0 .../alter_user_result_error_uid_secret.json | 0 .../alter_user_result_success_inst_data.xml | 0 ..._user_result_success_inst_data_secret.json | 0 ...alter_user_result_success_uid_secret.json} | 0 .../test_additional_secrets_redaction.py | 25 +++--- tests/common/test_common_constants.py | 44 +++++++++++ tests/data_set/test_data_set_constants.py | 6 -- tests/group/test_group_constants.py | 6 -- tests/resource/test_resource_constants.py | 12 --- tests/user/test_user_constants.py | 12 --- 22 files changed, 122 insertions(+), 89 deletions(-) rename tests/{data_set/data_set_result_samples => common/common_result_samples}/alter_data_set_result_error_uacc_secret.json (100%) rename tests/{data_set/data_set_result_samples => common/common_result_samples}/alter_data_set_result_success_owner_secret.json (100%) rename tests/{group/group_result_samples => common/common_result_samples}/alter_group_result_error_gid_secret.json (100%) rename tests/{group/group_result_samples => common/common_result_samples}/alter_group_result_success_gid_secret.json (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/alter_resource_overwrite_audit_result_success.xml (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/alter_resource_result_error_uacc_secret.json (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/alter_resource_result_success_audit_secret.json (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/alter_resource_result_success_owner_secret.json (100%) rename tests/{user/user_result_samples => common/common_result_samples}/alter_user_result_error_uid_secret.json (100%) rename tests/{user/user_result_samples => common/common_result_samples}/alter_user_result_success_inst_data.xml (100%) rename tests/{user/user_result_samples => common/common_result_samples}/alter_user_result_success_inst_data_secret.json (100%) rename tests/{user/user_result_samples/alter_user_result_extended_success.json => common/common_result_samples/alter_user_result_success_uid_secret.json} (100%) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index b6b807f3..8c341fb5 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -54,6 +54,10 @@ class SecurityAdmin: _valid_segment_traits = {} _extracted_key_value_pair_segment_traits_map = {} + _racf_key_map = {} + _racf_message_key_map = {} + _racf_key_map = {} + _racf_message_key_map = {} _case_sensitive_extracted_values = [] __running_userid = None _logger = Logger() @@ -119,6 +123,7 @@ def __init__( self._logger.log_experimental("Replace Existing Segment Traits") self.__replace_valid_segment_traits(replace_existing_segment_traits) if additional_secret_traits is not None: + self._logger.log_experimental("Add Additional Secret Traits") self.__add_additional_secret_traits(additional_secret_traits) self.set_running_userid(run_as_userid) @@ -169,7 +174,12 @@ def __raw_dump(self) -> None: if self.__debug: # Note, since the hex dump is logged to the console, # secrets will be redacted. - self._logger.log_hex_dump(raw_result_xml, self.__secret_traits) + self._logger.log_hex_dump( + raw_result_xml, + self.__secret_traits, + self._racf_key_map, + self._racf_message_key_map, + ) # ============================================================================ # Secrets Redaction @@ -244,6 +254,8 @@ def _make_request( self.__running_userid, ), self.__secret_traits, + self._racf_key_map, + self._racf_message_key_map, ) self.__clear_state(security_request) if isinstance(raw_result, list): diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index b6c48c51..13bd381b 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -11,16 +11,6 @@ class Logger: """Logging for pyRACF.""" - __racf_key_map = { - "audaltr": "audit", - "audcntl": "audit", - "audnone": "audit", - "audread": "audit", - "audupdt": "audit", - } - - __racf_message_key_map = {"uacc": "universal access"} - __ansi_reset = "\033[0m" __ansi_gray = "\033[2m" __ansi_green = "\033[32m" @@ -167,41 +157,36 @@ def __redact_string( This function employs the following regular expressions explained below - Regex 1 ("quoted") - This is designed to match the pattern TRAIT('value') by matching the TRAIT name: - {trait_key.upper()}, a variable (potentially zero) amount of spaces ( +){{0,}}, then the - ('value') portion which must start and end with (' and '), but can conceivably contain - any characters, but a negative lookbehind is used to look for any unescaped single quotes - .*?(? str: """ Redacts a list of specific secret traits in a result xml string. @@ -299,8 +286,9 @@ def redact_result_xml( return security_result for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - if racf_key in self.__racf_key_map: - racf_key = self.__racf_key_map[racf_key] + if racf_key in racf_key_map: + print(racf_key_map[racf_key]) + racf_key = racf_key_map[racf_key] if isinstance(security_result, bytes): match = re.search( rf"{racf_key.upper()}( +){{0,}}\(", security_result.decode("cp1047") @@ -310,8 +298,8 @@ def redact_result_xml( if not match: continue security_result = self.__redact_string(security_result, racf_key) - if racf_key in self.__racf_message_key_map: - racf_key = self.__racf_message_key_map[racf_key] + if racf_key in racf_message_key_map: + racf_key = racf_message_key_map[racf_key] if isinstance(security_result, bytes): security_result = re.sub( rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", @@ -457,7 +445,13 @@ def __indent_xml(self, minified_xml: str) -> str: indented_xml += f"{' ' * indent_level}{current_line}\n" return indented_xml[:-2] - def log_hex_dump(self, raw_result_xml: bytes, secret_traits: dict) -> None: + def log_hex_dump( + self, + raw_result_xml: bytes, + secret_traits: dict, + racf_key_map: dict, + racf_message_key_map: dict, + ) -> None: """ Log the raw result XML returned by IRRSMO00 as a hex dump. """ @@ -470,6 +464,8 @@ def log_hex_dump(self, raw_result_xml: bytes, secret_traits: dict) -> None: raw_result_xml = self.redact_result_xml( raw_result_xml, secret_traits, + racf_key_map, + racf_message_key_map, ) for byte in raw_result_xml: color_function = self.__green diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index 9233ee0c..e2348fea 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -63,6 +63,14 @@ def __init__( "dfp": {"dfp:owner": "racf:resowner", "dfp:ckds_data_key": "racf:datakey"}, "tme": {"tme:roles": "racf:roles"}, } + self._racf_key_map = { + "audaltr": "audit", + "audcntl": "audit", + "audnone": "audit", + "audread": "audit", + "audupdt": "audit", + } + self._racf_message_key_map = {"uacc": "universal access"} self._valid_segment_traits["base"].update( self._common_base_traits_data_set_generic ) diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index 940c37f3..1a404616 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -238,6 +238,14 @@ def __init__( "sigrequired": "signatureRequired", }, } + self._racf_key_map = { + "audaltr": "audit", + "audcntl": "audit", + "audnone": "audit", + "audread": "audit", + "audupdt": "audit", + } + self._racf_message_key_map = {"uacc": "universal access"} super().__init__( "resource", irrsmo00_result_buffer_size=irrsmo00_result_buffer_size, diff --git a/tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json b/tests/common/common_result_samples/alter_data_set_result_error_uacc_secret.json similarity index 100% rename from tests/data_set/data_set_result_samples/alter_data_set_result_error_uacc_secret.json rename to tests/common/common_result_samples/alter_data_set_result_error_uacc_secret.json diff --git a/tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json b/tests/common/common_result_samples/alter_data_set_result_success_owner_secret.json similarity index 100% rename from tests/data_set/data_set_result_samples/alter_data_set_result_success_owner_secret.json rename to tests/common/common_result_samples/alter_data_set_result_success_owner_secret.json diff --git a/tests/group/group_result_samples/alter_group_result_error_gid_secret.json b/tests/common/common_result_samples/alter_group_result_error_gid_secret.json similarity index 100% rename from tests/group/group_result_samples/alter_group_result_error_gid_secret.json rename to tests/common/common_result_samples/alter_group_result_error_gid_secret.json diff --git a/tests/group/group_result_samples/alter_group_result_success_gid_secret.json b/tests/common/common_result_samples/alter_group_result_success_gid_secret.json similarity index 100% rename from tests/group/group_result_samples/alter_group_result_success_gid_secret.json rename to tests/common/common_result_samples/alter_group_result_success_gid_secret.json diff --git a/tests/resource/resource_result_samples/alter_resource_overwrite_audit_result_success.xml b/tests/common/common_result_samples/alter_resource_overwrite_audit_result_success.xml similarity index 100% rename from tests/resource/resource_result_samples/alter_resource_overwrite_audit_result_success.xml rename to tests/common/common_result_samples/alter_resource_overwrite_audit_result_success.xml diff --git a/tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json b/tests/common/common_result_samples/alter_resource_result_error_uacc_secret.json similarity index 100% rename from tests/resource/resource_result_samples/alter_resource_result_error_uacc_secret.json rename to tests/common/common_result_samples/alter_resource_result_error_uacc_secret.json diff --git a/tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json b/tests/common/common_result_samples/alter_resource_result_success_audit_secret.json similarity index 100% rename from tests/resource/resource_result_samples/alter_resource_result_success_audit_secret.json rename to tests/common/common_result_samples/alter_resource_result_success_audit_secret.json diff --git a/tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json b/tests/common/common_result_samples/alter_resource_result_success_owner_secret.json similarity index 100% rename from tests/resource/resource_result_samples/alter_resource_result_success_owner_secret.json rename to tests/common/common_result_samples/alter_resource_result_success_owner_secret.json diff --git a/tests/user/user_result_samples/alter_user_result_error_uid_secret.json b/tests/common/common_result_samples/alter_user_result_error_uid_secret.json similarity index 100% rename from tests/user/user_result_samples/alter_user_result_error_uid_secret.json rename to tests/common/common_result_samples/alter_user_result_error_uid_secret.json diff --git a/tests/user/user_result_samples/alter_user_result_success_inst_data.xml b/tests/common/common_result_samples/alter_user_result_success_inst_data.xml similarity index 100% rename from tests/user/user_result_samples/alter_user_result_success_inst_data.xml rename to tests/common/common_result_samples/alter_user_result_success_inst_data.xml diff --git a/tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json b/tests/common/common_result_samples/alter_user_result_success_inst_data_secret.json similarity index 100% rename from tests/user/user_result_samples/alter_user_result_success_inst_data_secret.json rename to tests/common/common_result_samples/alter_user_result_success_inst_data_secret.json diff --git a/tests/user/user_result_samples/alter_user_result_extended_success.json b/tests/common/common_result_samples/alter_user_result_success_uid_secret.json similarity index 100% rename from tests/user/user_result_samples/alter_user_result_extended_success.json rename to tests/common/common_result_samples/alter_user_result_success_uid_secret.json diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py index e2d38510..b5954908 100644 --- a/tests/common/test_additional_secrets_redaction.py +++ b/tests/common/test_additional_secrets_redaction.py @@ -5,6 +5,7 @@ import __init__ +import tests.common.test_common_constants as TestCommonConstants import tests.data_set.test_data_set_constants as TestDataSetConstants import tests.group.test_group_constants as TestGroupConstants import tests.resource.test_resource_constants as TestResourceConstants @@ -43,7 +44,7 @@ def test_user_admin_custom_secret_redacted_on_success( ) self.assertEqual( result, - TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_DICTIONARY, + TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_UID_SECRET_DICTIONARY, ) self.assertNotIn( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], @@ -67,7 +68,7 @@ def test_user_admin_custom_secret_redacted_on_error( ) self.assertEqual( exception.exception.result, - TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY, ) self.assertNotIn( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"], @@ -82,7 +83,7 @@ def test_user_admin_custom_secret_redacted_when_complex_characters( user_admin = UserAdmin(additional_secret_traits=["base:installation_data"]) call_racf_mock.side_effect = [ TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, + TestCommonConstants.TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML, ] result = user_admin.alter( "squidwrd", @@ -90,7 +91,7 @@ def test_user_admin_custom_secret_redacted_when_complex_characters( ) self.assertEqual( result, - TestUserConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, ) self.assertNotIn( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ @@ -134,7 +135,7 @@ def test_group_admin_custom_secret_redacted_on_success( ) self.assertEqual( result, - TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY, ) self.assertNotIn( TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], @@ -158,7 +159,7 @@ def test_group_admin_custom_secret_redacted_on_error( ) self.assertEqual( exception.exception.result, - TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY, ) self.assertNotIn( TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"], @@ -200,7 +201,7 @@ def test_resource_admin_custom_secret_redacted_on_success( ) self.assertEqual( result, - TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, ) self.assertNotIn( TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS["base:owner"], @@ -215,7 +216,7 @@ def test_resource_admin_custom_mapped_secret_redacted_on_success( resource_admin = ResourceAdmin(additional_secret_traits=["base:audit_update"]) call_racf_mock.side_effect = [ TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, - TestResourceConstants.TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML, + TestCommonConstants.TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML, ] result = resource_admin.overwrite_audit_rules_by_access_level( "TESTING", @@ -224,7 +225,7 @@ def test_resource_admin_custom_mapped_secret_redacted_on_success( ) self.assertEqual( result, - TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_AUDIT_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_AUDIT_SECRET_DICTIONARY, ) self.assertNotIn( "ALL", @@ -250,7 +251,7 @@ def test_resource_admin_custom_secret_redacted_on_error( ) self.assertEqual( exception.exception.result, - TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY, ) self.assertNotIn( TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS[ @@ -295,7 +296,7 @@ def test_data_set_admin_custom_secret_redacted_on_success( ) self.assertEqual( result, - TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY, ) self.assertNotIn( TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS["base:owner"], @@ -320,7 +321,7 @@ def test_data_set_admin_custom_secret_redacted_on_error( ) self.assertEqual( exception.exception.result, - TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY, + TestCommonConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY, ) self.assertNotIn( TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS[f"{secret_trait}"], diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 7a0c83ff..869fdd39 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -97,6 +97,50 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "extract_user_result_base_omvs_csdata_success.json" ) +# ============================================================================ +# Additional Secrets Redaction +# ============================================================================ + +TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( + "alter_data_set_result_success_owner_secret.json" +) +TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( + "alter_data_set_result_error_uacc_secret.json" +) + +TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY = get_sample( + "alter_group_result_success_gid_secret.json" +) +TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY = get_sample( + "alter_group_result_error_gid_secret.json" +) + +TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML = get_sample( + "alter_resource_overwrite_audit_result_success.xml" +) +TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( + "alter_resource_result_success_owner_secret.json" +) +TEST_ALTER_RESOURCE_RESULT_SUCCESS_AUDIT_SECRET_DICTIONARY = get_sample( + "alter_resource_result_success_audit_secret.json" +) +TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( + "alter_resource_result_error_uacc_secret.json" +) + +TEST_ALTER_USER_RESULT_SUCCESS_UID_SECRET_DICTIONARY = get_sample( + "alter_user_result_success_uid_secret.json" +) +TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY = get_sample( + "alter_user_result_error_uid_secret.json" +) +TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML = get_sample( + "alter_user_result_success_inst_data.xml" +) +TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY = get_sample( + "alter_user_result_success_inst_data_secret.json" +) + # ============================================================================ # Run As UserId # ============================================================================ diff --git a/tests/data_set/test_data_set_constants.py b/tests/data_set/test_data_set_constants.py index 22af3969..cf050ab2 100644 --- a/tests/data_set/test_data_set_constants.py +++ b/tests/data_set/test_data_set_constants.py @@ -34,16 +34,10 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_DATA_SET_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_data_set_result_success.json" ) -TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( - "alter_data_set_result_success_owner_secret.json" -) TEST_ALTER_DATA_SET_RESULT_ERROR_XML = get_sample("alter_data_set_result_error.xml") TEST_ALTER_DATA_SET_RESULT_ERROR_DICTIONARY = get_sample( "alter_data_set_result_error.json" ) -TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( - "alter_data_set_result_error_uacc_secret.json" -) # Extract Data Set TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML = get_sample( diff --git a/tests/group/test_group_constants.py b/tests/group/test_group_constants.py index 5af92c68..478a1ca9 100644 --- a/tests/group/test_group_constants.py +++ b/tests/group/test_group_constants.py @@ -26,14 +26,8 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_GROUP_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_group_result_success.json" ) -TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY = get_sample( - "alter_group_result_success_gid_secret.json" -) TEST_ALTER_GROUP_RESULT_ERROR_XML = get_sample("alter_group_result_error.xml") TEST_ALTER_GROUP_RESULT_ERROR_DICTIONARY = get_sample("alter_group_result_error.json") -TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY = get_sample( - "alter_group_result_error_gid_secret.json" -) # Extract Group TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML = get_sample( diff --git a/tests/resource/test_resource_constants.py b/tests/resource/test_resource_constants.py index 2e071c51..a1448ccd 100644 --- a/tests/resource/test_resource_constants.py +++ b/tests/resource/test_resource_constants.py @@ -25,25 +25,13 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Alter Resource TEST_ALTER_RESOURCE_RESULT_SUCCESS_XML = get_sample("alter_resource_result_success.xml") -TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML = get_sample( - "alter_resource_overwrite_audit_result_success.xml" -) TEST_ALTER_RESOURCE_RESULT_SUCCESS_DICTIONARY = get_sample( "alter_resource_result_success.json" ) -TEST_ALTER_RESOURCE_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( - "alter_resource_result_success_owner_secret.json" -) -TEST_ALTER_RESOURCE_RESULT_SUCCESS_AUDIT_SECRET_DICTIONARY = get_sample( - "alter_resource_result_success_audit_secret.json" -) TEST_ALTER_RESOURCE_RESULT_ERROR_XML = get_sample("alter_resource_result_error.xml") TEST_ALTER_RESOURCE_RESULT_ERROR_DICTIONARY = get_sample( "alter_resource_result_error.json" ) -TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( - "alter_resource_result_error_uacc_secret.json" -) # Extract Resource TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML = get_sample( diff --git a/tests/user/test_user_constants.py b/tests/user/test_user_constants.py index d3676ae9..aed1d865 100644 --- a/tests/user/test_user_constants.py +++ b/tests/user/test_user_constants.py @@ -65,18 +65,6 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML = get_sample( "alter_user_result_extended_success.xml" ) -TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_DICTIONARY = get_sample( - "alter_user_result_extended_success.json" -) -TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY = get_sample( - "alter_user_result_error_uid_secret.json" -) -TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML = get_sample( - "alter_user_result_success_inst_data.xml" -) -TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY = get_sample( - "alter_user_result_success_inst_data_secret.json" -) # Extract User TEST_EXTRACT_USER_RESULT_BASE_OMVS_SUCCESS_XML = get_sample( From a2e6cefbc036b4a5efe9e2be176d1d0f87ffac67 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 1 Mar 2024 17:15:21 -0500 Subject: [PATCH 10/14] Addressed PR Comments -Moved Debug logging testing of secret traits to common -Re-architected racf_key_map and racf_message_key_map under 1 dictionary -changed comments -established SecretRedactionError Signed-off-by: Elijah Swift --- .../exceptions/secrets_redaction_error.py | 35 +++++ pyracf/common/security_admin.py | 23 ++-- pyracf/common/utilities/logger.py | 89 ++++++------- pyracf/data_set/data_set_admin.py | 6 +- pyracf/resource/resource_admin.py | 6 +- ...ter_user_additional_secret_added_error.log | 0 ...r_user_additional_secret_added_success.log | 0 .../test_additional_secrets_redaction.py | 123 +++++++++++++----- tests/common/test_common_constants.py | 17 +++ tests/test_runner.py | 6 +- tests/user/test_user_constants.py | 12 +- tests/user/test_user_debug_logging.py | 58 --------- 12 files changed, 214 insertions(+), 161 deletions(-) create mode 100644 pyracf/common/exceptions/secrets_redaction_error.py rename tests/{user/user_log_samples => common/common_log_samples}/alter_user_additional_secret_added_error.log (100%) rename tests/{user/user_log_samples => common/common_log_samples}/alter_user_additional_secret_added_success.log (100%) diff --git a/pyracf/common/exceptions/secrets_redaction_error.py b/pyracf/common/exceptions/secrets_redaction_error.py new file mode 100644 index 00000000..ce7729a5 --- /dev/null +++ b/pyracf/common/exceptions/secrets_redaction_error.py @@ -0,0 +1,35 @@ +"""Exception to use when Additional Secrets could not be Redacted.""" + +from typing import List + + +class SecretsRedactionError(Exception): + """ + Raised when a specified secret cannot be redacted because it does not map to a segement:trait. + """ + + def __init__( + self, profile_type: str = "", bad_secret_traits: List[str] = [] + ) -> None: + profile_map = { + "user": "User", + "group": "Group", + "dataSet": "Data Set", + "resource": "General Resource", + "permission": "Access", + "groupConnection": "Group Connection", + "systemSettings": "Setropts", + } + self.message = ( + f"Cannot add specified additional secrets to {profile_map[profile_type]} " + + "administration." + ) + + if bad_secret_traits: + for trait in bad_secret_traits: + self.message = self.message + ( + f"\nCould not map {trait} to a valid segment trait." + ) + + def __str__(self) -> str: + return self.message diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 8c341fb5..75630921 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -7,6 +7,7 @@ from typing import Any, List, Tuple, Union from .exceptions.downstream_fatal_error import DownstreamFatalError +from .exceptions.secrets_redaction_error import SecretsRedactionError from .exceptions.security_request_error import SecurityRequestError from .exceptions.segment_error import SegmentError from .exceptions.segment_trait_error import SegmentTraitError @@ -54,10 +55,8 @@ class SecurityAdmin: _valid_segment_traits = {} _extracted_key_value_pair_segment_traits_map = {} - _racf_key_map = {} - _racf_message_key_map = {} - _racf_key_map = {} - _racf_message_key_map = {} + # Use this structure to map traits to their RACF keywords and Message keywords for redaction + _racf_trait_and_message_key_map = {"trait_map": {}, "message_map": {}} _case_sensitive_extracted_values = [] __running_userid = None _logger = Logger() @@ -177,8 +176,7 @@ def __raw_dump(self) -> None: self._logger.log_hex_dump( raw_result_xml, self.__secret_traits, - self._racf_key_map, - self._racf_message_key_map, + self._racf_trait_and_message_key_map, ) # ============================================================================ @@ -188,18 +186,26 @@ def __add_additional_secret_traits(self, additional_secret_traits: list) -> None """Add additional fields to be redacted in logger output.""" unsupported_profile_types = ["permission", "groupConnection", "systemSettings"] if self._profile_type in unsupported_profile_types: - return + raise SecretsRedactionError(profile_type=self._profile_type) + bad_secret_traits = [] for secret in additional_secret_traits: if secret in self.__secret_traits: continue if ":" not in secret: + bad_secret_traits.append(secret) continue segment = secret.split(":")[0] if segment not in self._valid_segment_traits: + bad_secret_traits.append(secret) continue if secret not in self._valid_segment_traits[segment]: + bad_secret_traits.append(secret) continue self.__secret_traits[secret] = self._valid_segment_traits[segment][secret] + if bad_secret_traits: + raise SecretsRedactionError( + profile_type=self._profile_type, bad_secret_traits=bad_secret_traits + ) # ============================================================================ # Request Execution @@ -254,8 +260,7 @@ def _make_request( self.__running_userid, ), self.__secret_traits, - self._racf_key_map, - self._racf_message_key_map, + self._racf_trait_and_message_key_map, ) self.__clear_state(security_request) if isinstance(raw_result, list): diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 13bd381b..6e36fead 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -168,10 +168,10 @@ def __redact_string( This is designed to match the pattern TRAIT( subtrait1(value) subtrait2(value)) by matching the TRAIT name, a variable (potentially zero) amount of spaces, then the ( subtrait1(value) subtrait2(value)) portion which must start and end with ( and ), - but must also contain a nested set of opened parentheses rather than a direct seqence of - them. The pattern looks for these nested open parenthesis as a sequence would have a ) - character between them. Then the expression allows any non-newline characters and the - "end pattern" of ) and ) separated by a variable (potentially zero) whitespace. + but must also contain a nested set of open and close parentheses rather than a direct + seqence of them. The pattern looks for these nested open parenthesis as a sequence would + have a ) character between them. Then the expression allows any non-newline characters and + the "end pattern" of ) and ) separated by a variable (potentially zero) whitespace. If neither of these two patterns is found for a supplied trait_key, then it is assumed this trait is set with the default pattern below. @@ -180,7 +180,7 @@ def __redact_string( This is designed to match the pattern TRAIT(value) by matching the TRAIT name, a variable (potentially zero) amount of spaces, then the (value) portion which must start and end with ( and ), but can conceivably contain any characters, but a negative lookbehind - is used to look for any escape \ character that would indicate the matching of the + is used to look for any escape character `\` that would indicate the matching of the ( and ) is otherwise a coincidence. In all replacement expressions, the variable amounts of whitespace are captured so that @@ -189,32 +189,33 @@ def __redact_string( """ asterisks = "********" is_bytes = False + quoted_regex = rf"{trait_key.upper()}( +){{0,}}\(\'.*?(?.*<\/{xml_key} - Designed to match the above pattern by starting and ending - with the xmltag string as shown, but the starting tage allows for any characters between + with the xmltag string as shown, but the starting tag allows for any characters between "xmltag" and the > character to allow for the attribute specification shown above. This results in the starting of the xml as {xml_key}(.*)> and the ending as <\/{xml_key}. - The miscellaneous characters are "captured" as a variable and preserved by the + The characters between the xml tags are "captured" as a variable and preserved by the substitution operation through the use of the \1 supplied in the replacement string. Between these tags, any non-newline characters are allowed using the .* expression. """ @@ -246,9 +247,8 @@ def redact_request_xml( is_bytes = True xml_string = xml_string.decode("utf-8") for xml_key in secret_traits.values(): - match = re.search(rf"\<{xml_key}+[^>]*\>", xml_string) - if not match: - continue + start_tag_eng_tag_regex = rf"{xml_key}(.*)>.*<\/{xml_key}" + redacted_regex = rf"{xml_key}\1>******** - if f"" not in xml_string: - continue xml_string = re.sub( - rf"{xml_key}(.*)>.*<\/{xml_key}", - rf"{xml_key}\1>******** str: """ Redacts a list of specific secret traits in a result xml string. @@ -286,32 +283,38 @@ def redact_result_xml( return security_result for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - if racf_key in racf_key_map: - print(racf_key_map[racf_key]) - racf_key = racf_key_map[racf_key] + if racf_key in racf_trait_and_message_key_map.get("trait_map", {}): + racf_key = racf_trait_and_message_key_map["trait_map"][racf_key] + racf_command_argument_regex = rf"{racf_key.upper()}( +){{0,}}\(" if isinstance(security_result, bytes): match = re.search( - rf"{racf_key.upper()}( +){{0,}}\(", security_result.decode("cp1047") + racf_command_argument_regex, security_result.decode("cp1047") ) else: match = re.search(rf"{racf_key.upper()}( +){{0,}}\(", security_result) if not match: continue security_result = self.__redact_string(security_result, racf_key) - if racf_key in racf_message_key_map: - racf_key = racf_message_key_map[racf_key] + if racf_key in racf_trait_and_message_key_map.get("message_map", {}): + racf_key = racf_trait_and_message_key_map["message_map"][racf_key] + racf_message_regex = ( + r"([A-Z]*[0-9]*[A-Z]) [^<>]*" + + rf"{racf_key.upper()}[^<>]*<\/message>" + ) + redacted_racf_message_regex = ( + rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " + + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION" + ) if isinstance(security_result, bytes): security_result = re.sub( - rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", - rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " - + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION", + racf_message_regex, + redacted_racf_message_regex, security_result.decode("cp1047"), ).encode("cp1047") else: security_result = re.sub( - rf"([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>", - rf"REDACTED MESSAGE CONCERNING {racf_key.upper()}, " - + r"REVIEW DOCUMENTATION OF \1 FOR MORE INFORMATION", + racf_message_regex, + redacted_racf_message_regex, security_result, ) return security_result @@ -449,8 +452,7 @@ def log_hex_dump( self, raw_result_xml: bytes, secret_traits: dict, - racf_key_map: dict, - racf_message_key_map: dict, + racf_trait_and_message_key_map: dict, ) -> None: """ Log the raw result XML returned by IRRSMO00 as a hex dump. @@ -464,8 +466,7 @@ def log_hex_dump( raw_result_xml = self.redact_result_xml( raw_result_xml, secret_traits, - racf_key_map, - racf_message_key_map, + racf_trait_and_message_key_map, ) for byte in raw_result_xml: color_function = self.__green diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index e2348fea..2b93359a 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -63,14 +63,16 @@ def __init__( "dfp": {"dfp:owner": "racf:resowner", "dfp:ckds_data_key": "racf:datakey"}, "tme": {"tme:roles": "racf:roles"}, } - self._racf_key_map = { + self._racf_trait_and_message_key_map["trait_map"] = { "audaltr": "audit", "audcntl": "audit", "audnone": "audit", "audread": "audit", "audupdt": "audit", } - self._racf_message_key_map = {"uacc": "universal access"} + self._racf_trait_and_message_key_map["message_map"] = { + "uacc": "universal access" + } self._valid_segment_traits["base"].update( self._common_base_traits_data_set_generic ) diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index 1a404616..39d23173 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -238,14 +238,16 @@ def __init__( "sigrequired": "signatureRequired", }, } - self._racf_key_map = { + self._racf_trait_and_message_key_map["trait_map"] = { "audaltr": "audit", "audcntl": "audit", "audnone": "audit", "audread": "audit", "audupdt": "audit", } - self._racf_message_key_map = {"uacc": "universal access"} + self._racf_trait_and_message_key_map["message_map"] = { + "uacc": "universal access" + } super().__init__( "resource", irrsmo00_result_buffer_size=irrsmo00_result_buffer_size, diff --git a/tests/user/user_log_samples/alter_user_additional_secret_added_error.log b/tests/common/common_log_samples/alter_user_additional_secret_added_error.log similarity index 100% rename from tests/user/user_log_samples/alter_user_additional_secret_added_error.log rename to tests/common/common_log_samples/alter_user_additional_secret_added_error.log diff --git a/tests/user/user_log_samples/alter_user_additional_secret_added_success.log b/tests/common/common_log_samples/alter_user_additional_secret_added_success.log similarity index 100% rename from tests/user/user_log_samples/alter_user_additional_secret_added_success.log rename to tests/common/common_log_samples/alter_user_additional_secret_added_success.log diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py index b5954908..1f296775 100644 --- a/tests/common/test_additional_secrets_redaction.py +++ b/tests/common/test_additional_secrets_redaction.py @@ -1,5 +1,8 @@ """Test customizing security admin segment traits.""" +import contextlib +import io +import re import unittest from unittest.mock import Mock, patch @@ -22,7 +25,7 @@ __init__ -class TestAdditionalSecretsRedaction(unittest.TestCase): +class TestAdditionalSecretsResultRedaction(unittest.TestCase): maxDiff = None # ============================================================================ @@ -48,7 +51,7 @@ def test_user_admin_custom_secret_redacted_on_success( ) self.assertNotIn( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], - result, + str(result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -71,8 +74,8 @@ def test_user_admin_custom_secret_redacted_on_error( TestCommonConstants.TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY, ) self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"], - exception.exception.result, + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"]), + str(exception.exception.result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -87,17 +90,17 @@ def test_user_admin_custom_secret_redacted_when_complex_characters( ] result = user_admin.alter( "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, + traits=TestCommonConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA, ) self.assertEqual( result, TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY, ) self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ + TestCommonConstants.TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA[ "base:installation_data" ], - result, + str(result), ) def test_user_admin_custom_secret_redacted_request(self): @@ -109,11 +112,8 @@ def test_user_admin_custom_secret_redacted_request(self): traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, ) self.assertNotIn( - bytes( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], - "utf-8", - ), - result, + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + str(result), ) # ============================================================================ @@ -139,7 +139,7 @@ def test_group_admin_custom_secret_redacted_on_success( ) self.assertNotIn( TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], - result, + str(result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -163,7 +163,7 @@ def test_group_admin_custom_secret_redacted_on_error( ) self.assertNotIn( TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"], - exception.exception.result, + str(exception.exception.result), ) def test_group_admin_custom_secret_redacted_request(self): @@ -175,10 +175,8 @@ def test_group_admin_custom_secret_redacted_request(self): traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS, ) self.assertNotIn( - bytes( - TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], "utf-8" - ), - result, + TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], + str(result), ) # ============================================================================ @@ -205,7 +203,7 @@ def test_resource_admin_custom_secret_redacted_on_success( ) self.assertNotIn( TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS["base:owner"], - result, + str(result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -229,7 +227,7 @@ def test_resource_admin_custom_mapped_secret_redacted_on_success( ) self.assertNotIn( "ALL", - result, + str(result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -257,7 +255,7 @@ def test_resource_admin_custom_secret_redacted_on_error( TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS[ f"{secret_trait}" ], - exception.exception.result, + str(exception.exception.result), ) def test_resource_admin_custom_secret_redacted_request(self): @@ -270,11 +268,8 @@ def test_resource_admin_custom_secret_redacted_request(self): traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS, ) self.assertNotIn( - bytes( - TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS["base:owner"], - "utf-8", - ), - result, + TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS["base:owner"], + str(result), ) # ============================================================================ @@ -300,7 +295,7 @@ def test_data_set_admin_custom_secret_redacted_on_success( ) self.assertNotIn( TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS["base:owner"], - result, + str(result), ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -325,7 +320,7 @@ def test_data_set_admin_custom_secret_redacted_on_error( ) self.assertNotIn( TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS[f"{secret_trait}"], - exception.exception.result, + str(exception.exception.result), ) def test_data_set_admin_custom_secret_redacted_request(self): @@ -337,9 +332,71 @@ def test_data_set_admin_custom_secret_redacted_request(self): traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, ) self.assertNotIn( - bytes( - TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS["base:owner"], - "utf-8", - ), - result, + TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS["base:owner"], + str(result), + ) + + +@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") +class TestAdditionalSecretsLoggingRedaction(unittest.TestCase): + maxDiff = None + user_admin = UserAdmin(debug=True) + ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + # ============================================================================ + # Add Additional Secrets + # ============================================================================ + def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_success( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, + ) + self.assertNotIn( + TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + success_log, + ) + + # Secret redacted from command image but not from resulting error message. + # Marked experimental until resolved + def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_error( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, + ) + except SecurityRequestError: + pass + error_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + error_log, + TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG, + ) + self.assertNotIn( + f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", + error_log, ) diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 869fdd39..c83f51a4 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -101,6 +101,16 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Additional Secrets Redaction # ============================================================================ +TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA = { + "base:name": "Squidward", + "base:owner": "leonard", + "base:special": True, + "omvs:uid": "2424", + "omvs:home_directory": "/u/squidwrd", + "omvs:default_shell": "/bin/sh", + "base:installation_data": "Test = Value; Other(stuff goes here)'')", +} + TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( "alter_data_set_result_success_owner_secret.json" ) @@ -141,6 +151,13 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "alter_user_result_success_inst_data_secret.json" ) +TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( + "alter_user_additional_secret_added_success.log" +) +TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( + "alter_user_additional_secret_added_error.log" +) + # ============================================================================ # Run As UserId # ============================================================================ diff --git a/tests/test_runner.py b/tests/test_runner.py index c1845637..4dcf5e3b 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -11,7 +11,8 @@ from tests.access.test_access_request_builder import TestAccessRequestBuilder from tests.access.test_access_result_parser import TestAccessResultParser from tests.common.test_additional_secrets_redaction import ( - TestAdditionalSecretsRedaction, + TestAdditionalSecretsLoggingRedaction, + TestAdditionalSecretsResultRedaction, ) from tests.common.test_class_attributes import TestClassAttributes from tests.common.test_customize_segment_traits import TestCustomizeSegmentTraits @@ -72,7 +73,8 @@ def __test_suite() -> unittest.TestSuite: TestAccessResultParser, TestAccessRequestBuilder, TestAccessDebugLogging, - TestAdditionalSecretsRedaction, + TestAdditionalSecretsResultRedaction, + TestAdditionalSecretsLoggingRedaction, TestClassAttributes, TestCustomizeSegmentTraits, TestSetupPrecheck, diff --git a/tests/user/test_user_constants.py b/tests/user/test_user_constants.py index aed1d865..d881e241 100644 --- a/tests/user/test_user_constants.py +++ b/tests/user/test_user_constants.py @@ -212,10 +212,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR = dict(TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED) TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"] = 90000000000 -TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA = dict(TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED) -TEST_ALTER_USER_REQUEST_TRAITS_INST_DATA["base:installation_data"] = ( - "Test = Value; Other(stuff goes here)'')" -) + # Extract User TEST_EXTRACT_USER_REQUEST_BASE_OMVS_XML = get_sample( @@ -419,13 +416,6 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_USER_SUCCESS_LOG = get_sample("alter_user_success.log") TEST_ALTER_USER_ERROR_LOG = get_sample("alter_user_error.log") -TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( - "alter_user_additional_secret_added_success.log" -) -TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( - "alter_user_additional_secret_added_error.log" -) - TEST_ALTER_USER_PASSWORD_SUCCESS_LOG = get_sample("alter_user_password_success.log") TEST_ALTER_USER_PASSWORD_ERROR_LOG = get_sample("alter_user_password_error.log") diff --git a/tests/user/test_user_debug_logging.py b/tests/user/test_user_debug_logging.py index efb6f64e..e7fe6c02 100644 --- a/tests/user/test_user_debug_logging.py +++ b/tests/user/test_user_debug_logging.py @@ -261,64 +261,6 @@ def test_alter_user_request_debug_log_password_xml_tags_not_redacted_on_error( self.assertEqual(error_log.count("********"), 4) self.assertIn(self.simple_password, error_log) - # ============================================================================ - # Add Additional Secrets - # ============================================================================ - def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_success( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, - ] - stdout = io.StringIO() - with contextlib.redirect_stdout(stdout): - user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, - ) - success_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual( - success_log, - TestUserConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, - ) - self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], - success_log, - ) - - # Secret redacted from command image but not from resulting error message. - # Marked experimental until resolved - def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_error( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, - ] - stdout = io.StringIO() - with contextlib.redirect_stdout(stdout): - try: - user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, - ) - except SecurityRequestError: - pass - error_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual( - error_log, - TestUserConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG, - ) - self.assertNotIn( - f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", - error_log, - ) - # ============================================================================ # Extract User # ============================================================================ From 6a3be62308484822a42e19edc7d503d8d9d28152 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 1 Mar 2024 17:18:47 -0500 Subject: [PATCH 11/14] Update secrets_redaction_error.py Change Secrets Redaction Error message for invalid admin function Signed-off-by: Elijah Swift --- pyracf/common/exceptions/secrets_redaction_error.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyracf/common/exceptions/secrets_redaction_error.py b/pyracf/common/exceptions/secrets_redaction_error.py index ce7729a5..003d4471 100644 --- a/pyracf/common/exceptions/secrets_redaction_error.py +++ b/pyracf/common/exceptions/secrets_redaction_error.py @@ -30,6 +30,11 @@ def __init__( self.message = self.message + ( f"\nCould not map {trait} to a valid segment trait." ) + else: + self.message = self.message + ( + f"\n{profile_map[profile_type]} administration does" + + " not support additional secrets redaction." + ) def __str__(self) -> str: return self.message From a61fe8eb32c41c3f60f397f38815dda4e1a1eb58 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Mar 2024 11:49:43 -0500 Subject: [PATCH 12/14] Address latest round of PR comments -Change error type to ValueError -Address typos and comment fixes -Ensure AssertNotIn tests use proper strings Signed-off-by: Elijah Swift --- .../exceptions/secrets_redaction_error.py | 40 ------------------- pyracf/common/security_admin.py | 36 +++++++++++++---- pyracf/common/utilities/logger.py | 32 ++++++++++----- .../test_additional_secrets_redaction.py | 24 ++++++----- 4 files changed, 63 insertions(+), 69 deletions(-) delete mode 100644 pyracf/common/exceptions/secrets_redaction_error.py diff --git a/pyracf/common/exceptions/secrets_redaction_error.py b/pyracf/common/exceptions/secrets_redaction_error.py deleted file mode 100644 index 003d4471..00000000 --- a/pyracf/common/exceptions/secrets_redaction_error.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Exception to use when Additional Secrets could not be Redacted.""" - -from typing import List - - -class SecretsRedactionError(Exception): - """ - Raised when a specified secret cannot be redacted because it does not map to a segement:trait. - """ - - def __init__( - self, profile_type: str = "", bad_secret_traits: List[str] = [] - ) -> None: - profile_map = { - "user": "User", - "group": "Group", - "dataSet": "Data Set", - "resource": "General Resource", - "permission": "Access", - "groupConnection": "Group Connection", - "systemSettings": "Setropts", - } - self.message = ( - f"Cannot add specified additional secrets to {profile_map[profile_type]} " - + "administration." - ) - - if bad_secret_traits: - for trait in bad_secret_traits: - self.message = self.message + ( - f"\nCould not map {trait} to a valid segment trait." - ) - else: - self.message = self.message + ( - f"\n{profile_map[profile_type]} administration does" - + " not support additional secrets redaction." - ) - - def __str__(self) -> str: - return self.message diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 75630921..95408a8a 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -7,7 +7,6 @@ from typing import Any, List, Tuple, Union from .exceptions.downstream_fatal_error import DownstreamFatalError -from .exceptions.secrets_redaction_error import SecretsRedactionError from .exceptions.security_request_error import SecurityRequestError from .exceptions.segment_error import SegmentError from .exceptions.segment_trait_error import SegmentTraitError @@ -184,28 +183,49 @@ def __raw_dump(self) -> None: # ============================================================================ def __add_additional_secret_traits(self, additional_secret_traits: list) -> None: """Add additional fields to be redacted in logger output.""" + profile_map = { + "user": "User", + "group": "Group", + "dataSet": "Data Set", + "resource": "General Resource", + "permission": "Access", + "groupConnection": "Group Connection", + "systemSettings": "Setropts", + } unsupported_profile_types = ["permission", "groupConnection", "systemSettings"] + error_message = ( + f"Cannot add specified additional secrets to {profile_map[self._profile_type]} " + + "administration." + ) if self._profile_type in unsupported_profile_types: - raise SecretsRedactionError(profile_type=self._profile_type) + error_message = error_message + ( + f"\n{profile_map[self._profile_type]} administration does" + + " not support additional secrets redaction." + ) + raise ValueError(error_message) bad_secret_traits = [] for secret in additional_secret_traits: if secret in self.__secret_traits: continue if ":" not in secret: - bad_secret_traits.append(secret) + bad_secret_traits.append( + f"\nCould not map {secret} to a valid segment trait." + ) continue segment = secret.split(":")[0] if segment not in self._valid_segment_traits: - bad_secret_traits.append(secret) + bad_secret_traits.append( + f"\nCould not map {secret} to a valid segment trait." + ) continue if secret not in self._valid_segment_traits[segment]: - bad_secret_traits.append(secret) + bad_secret_traits.append( + f"\nCould not map {secret} to a valid segment trait." + ) continue self.__secret_traits[secret] = self._valid_segment_traits[segment][secret] if bad_secret_traits: - raise SecretsRedactionError( - profile_type=self._profile_type, bad_secret_traits=bad_secret_traits - ) + raise ValueError(error_message + "".join(bad_secret_traits)) # ============================================================================ # Request Execution diff --git a/pyracf/common/utilities/logger.py b/pyracf/common/utilities/logger.py index 6e36fead..fe6877f4 100644 --- a/pyracf/common/utilities/logger.py +++ b/pyracf/common/utilities/logger.py @@ -168,10 +168,14 @@ def __redact_string( This is designed to match the pattern TRAIT( subtrait1(value) subtrait2(value)) by matching the TRAIT name, a variable (potentially zero) amount of spaces, then the ( subtrait1(value) subtrait2(value)) portion which must start and end with ( and ), - but must also contain a nested set of open and close parentheses rather than a direct - seqence of them. The pattern looks for these nested open parenthesis as a sequence would - have a ) character between them. Then the expression allows any non-newline characters and - the "end pattern" of ) and ) separated by a variable (potentially zero) whitespace. + but must also contain a'(' before the ')'. This indicates that there is a "nested" + structure rather than a sequential one. In the example, this portion of the pattern + matches '( subtrait1(', but would not match '(value) subtrait2(' because of the ')' + character between the two '(' characters. The pattern looks for these nested open + parenthesis as a sequence would have a ')' character between them. Then the expression + allows any non-newline characters to handle all possible trait values and subtrait names + followed by the "end pattern" of ')' and ')' separated by a variable (potentially zero) + whitespace. If neither of these two patterns is found for a supplied trait_key, then it is assumed this trait is set with the default pattern below. @@ -179,9 +183,9 @@ def __redact_string( Regex 3 ("default") - {trait_key.upper()}( +){{0,}}\(.*?(?.*<\/{xml_key}" + start_tag_end_tag_regex = rf"{xml_key}(.*)>.*<\/{xml_key}" redacted_regex = rf"{xml_key}\1>******** xml_string = re.sub( - start_tag_eng_tag_regex, + start_tag_end_tag_regex, redacted_regex, xml_string, ) @@ -271,13 +275,21 @@ def redact_result_xml( secret_traits: dict, racf_trait_and_message_key_map: dict = {}, ) -> str: - """ + r""" Redacts a list of specific secret traits in a result xml string. Based on the following RACF command patterns: 'TRAIT (value)' 'TRAIT (subtrait1(value) subtrait2(value)) "TRAIT ('value')" This function also accounts for varied amounts of whitespace in the pattern. + + This function employs the following regular expression: + ([A-Z]*[0-9]*[A-Z]) [^<>]*{racf_key.upper()}[^<>]*<\/message>" - + Designed to match the above pattern by starting and ending with the message xml tag + string as shown, the value of the message is targeted based on the racf_key. This + should capture only messages that contain information about a redacted key. + The message identifier is "captured" as a variable and preserved by the substitution + operation through the use of the \1 supplied in the replacement string. """ if isinstance(security_result, list): return security_result diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py index 1f296775..68468912 100644 --- a/tests/common/test_additional_secrets_redaction.py +++ b/tests/common/test_additional_secrets_redaction.py @@ -50,7 +50,7 @@ def test_user_admin_custom_secret_redacted_on_success( TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_UID_SECRET_DICTIONARY, ) self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"]), str(result), ) @@ -112,7 +112,7 @@ def test_user_admin_custom_secret_redacted_request(self): traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, ) self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"]), str(result), ) @@ -138,7 +138,7 @@ def test_group_admin_custom_secret_redacted_on_success( TestCommonConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY, ) self.assertNotIn( - TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], + str(TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"]), str(result), ) @@ -162,7 +162,7 @@ def test_group_admin_custom_secret_redacted_on_error( TestCommonConstants.TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY, ) self.assertNotIn( - TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"], + str(TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"]), str(exception.exception.result), ) @@ -175,7 +175,7 @@ def test_group_admin_custom_secret_redacted_request(self): traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS, ) self.assertNotIn( - TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"], + str(TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"]), str(result), ) @@ -252,9 +252,11 @@ def test_resource_admin_custom_secret_redacted_on_error( TestCommonConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY, ) self.assertNotIn( - TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS[ - f"{secret_trait}" - ], + str( + TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS[ + secret_trait + ] + ), str(exception.exception.result), ) @@ -319,7 +321,7 @@ def test_data_set_admin_custom_secret_redacted_on_error( TestCommonConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY, ) self.assertNotIn( - TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS[f"{secret_trait}"], + TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS[secret_trait], str(exception.exception.result), ) @@ -367,7 +369,7 @@ def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_su TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, ) self.assertNotIn( - TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"], + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"]), success_log, ) @@ -397,6 +399,6 @@ def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_er TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG, ) self.assertNotIn( - f"({TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR['omvs:uid']})", + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"]), error_log, ) From 58e81c13b0074561772bf264ad73281ba76c8b55 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Mar 2024 13:38:28 -0500 Subject: [PATCH 13/14] Add Unit Testing for ValueError Add Unit Testing for ValueError in Secrets Redaction Tweak ValueError Text Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 15 +------ .../test_additional_secrets_redaction.py | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 95408a8a..6a2b58b2 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -183,24 +183,13 @@ def __raw_dump(self) -> None: # ============================================================================ def __add_additional_secret_traits(self, additional_secret_traits: list) -> None: """Add additional fields to be redacted in logger output.""" - profile_map = { - "user": "User", - "group": "Group", - "dataSet": "Data Set", - "resource": "General Resource", - "permission": "Access", - "groupConnection": "Group Connection", - "systemSettings": "Setropts", - } unsupported_profile_types = ["permission", "groupConnection", "systemSettings"] error_message = ( - f"Cannot add specified additional secrets to {profile_map[self._profile_type]} " - + "administration." + f"Cannot add specified additional secrets to '{self._profile_type}' object." ) if self._profile_type in unsupported_profile_types: error_message = error_message + ( - f"\n{profile_map[self._profile_type]} administration does" - + " not support additional secrets redaction." + f"\n'{self._profile_type}' object does not support additional secrets redaction." ) raise ValueError(error_message) bad_secret_traits = [] diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py index 68468912..b142bdc8 100644 --- a/tests/common/test_additional_secrets_redaction.py +++ b/tests/common/test_additional_secrets_redaction.py @@ -14,10 +14,13 @@ import tests.resource.test_resource_constants as TestResourceConstants import tests.user.test_user_constants as TestUserConstants from pyracf import ( + AccessAdmin, + ConnectionAdmin, DataSetAdmin, GroupAdmin, ResourceAdmin, SecurityRequestError, + SetroptsAdmin, UserAdmin, ) @@ -338,6 +341,48 @@ def test_data_set_admin_custom_secret_redacted_request(self): str(result), ) + def test_incompatible_admin_custom_secret_redaction_error(self): + admin_types = [ + (AccessAdmin, "permission"), + (ConnectionAdmin, "groupConnection"), + (SetroptsAdmin, "systemSettings"), + ] + for admin_type, profile_type in admin_types: + with self.assertRaises(ValueError) as exception: + admin_type(additional_secret_traits=["base:name"]) + self.assertEqual( + str(exception.exception), + f"Cannot add specified additional secrets to '{profile_type}' object.\n" + f"'{profile_type}' object does not support additional secrets redaction.", + ) + + def test_compatible_admin_incompatible_traits_custom_secret_redaction_error(self): + admin_types = [ + (UserAdmin, "user"), + (GroupAdmin, "group"), + (ResourceAdmin, "resource"), + (DataSetAdmin, "dataSet"), + ] + incompatible_traits = [ + "nosegment", + "incompatible:trait", + "omvs:name", + ] + incompatible_traits_text = "\n".join( + [ + f"Could not map {trait} to a valid segment trait." + for trait in incompatible_traits + ] + ) + for admin_type, profile_type in admin_types: + with self.assertRaises(ValueError) as exception: + admin_type(additional_secret_traits=incompatible_traits) + self.assertEqual( + str(exception.exception), + f"Cannot add specified additional secrets to '{profile_type}' object.\n" + + incompatible_traits_text, + ) + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") class TestAdditionalSecretsLoggingRedaction(unittest.TestCase): From f01f4773e5a675add423d7e5f53c8b9588d5913a Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Mar 2024 14:54:04 -0500 Subject: [PATCH 14/14] Add more unit tests Add debug logging redaction unit testing for other function groups Fix some underlying bugs/inconsistencies in other unit testing. Signed-off-by: Elijah Swift --- ...data_set_additional_secret_added_error.log | 268 ++++++++++++++++ ...ta_set_additional_secret_added_success.log | 252 +++++++++++++++ ...er_group_additional_secret_added_error.log | 246 +++++++++++++++ ..._group_additional_secret_added_success.log | 238 +++++++++++++++ ...resource_additional_secret_added_error.log | 257 ++++++++++++++++ ...source_additional_secret_added_success.log | 253 +++++++++++++++ .../alter_group_result_error_gid_secret.json | 2 +- ...alter_group_result_success_gid_secret.json | 2 +- .../test_additional_secrets_redaction.py | 289 ++++++++++++++---- tests/common/test_common_constants.py | 52 ++-- .../connect_connection_error.log | 6 +- .../connect_connection_success.log | 6 +- .../connect_connection_request.xml | 2 +- ...connection_give_group_access_attribute.xml | 2 +- ...onnection_give_group_auditor_authority.xml | 2 +- ...ection_give_group_operations_authority.xml | 2 +- ...onnection_give_group_special_authority.xml | 2 +- ...ction_take_away_group_access_attribute.xml | 2 +- ...tion_take_away_group_auditor_authority.xml | 2 +- ...n_take_away_group_operations_authority.xml | 2 +- ...tion_take_away_group_special_authority.xml | 2 +- .../delete_connection_request.xml | 2 +- .../connect_connection_result_error.json | 2 +- .../connect_connection_result_error.xml | 2 +- .../connect_connection_result_success.json | 2 +- .../connect_connection_result_success.xml | 2 +- .../delete_connection_result_error.json | 2 +- .../delete_connection_result_error.xml | 2 +- .../delete_connection_result_success.json | 2 +- .../delete_connection_result_success.xml | 2 +- .../test_connection_debug_logging.py | 4 +- .../test_connection_request_builder.py | 4 +- .../test_connection_result_parser.py | 8 +- tests/connection/test_connection_setters.py | 16 +- .../alter_data_set_error.log | 4 +- tests/data_set/test_data_set_constants.py | 4 + tests/data_set/test_data_set_debug_logging.py | 2 +- .../group_log_samples/alter_group_error.log | 14 +- .../group_log_samples/alter_group_success.log | 14 +- .../extract_group_base_omvs_error.log | 6 +- .../extract_group_base_omvs_success.log | 8 +- .../add_group_request.xml | 2 +- .../alter_group_request.xml | 2 +- .../delete_group_request.xml | 2 +- .../extract_group_request_base_omvs.xml | 2 +- .../group_set_omvs_gid.xml | 2 +- .../group_set_ovm_gid.xml | 2 +- .../add_group_result_success.json | 2 +- .../add_group_result_success.xml | 2 +- .../alter_group_result_error.json | 2 +- .../alter_group_result_error.xml | 2 +- .../alter_group_result_success.json | 2 +- .../alter_group_result_success.xml | 2 +- .../delete_group_result_error.json | 2 +- .../delete_group_result_error.xml | 2 +- .../delete_group_result_success.json | 2 +- .../delete_group_result_success.xml | 2 +- .../extract_group_result_base_omvs_error.json | 2 +- .../extract_group_result_base_omvs_error.xml | 2 +- ...xtract_group_result_base_omvs_success.json | 2 +- ...extract_group_result_base_omvs_success.xml | 2 +- .../extract_group_result_base_only_error.json | 2 +- .../extract_group_result_base_only_error.xml | 2 +- ...xtract_group_result_base_only_success.json | 2 +- ...extract_group_result_base_only_success.xml | 2 +- tests/group/test_group_debug_logging.py | 8 +- tests/group/test_group_getters.py | 36 +-- tests/group/test_group_request_builder.py | 8 +- tests/group/test_group_result_parser.py | 22 +- tests/group/test_group_setters.py | 4 +- tests/test_runner.py | 2 - 71 files changed, 1905 insertions(+), 214 deletions(-) create mode 100644 tests/common/common_log_samples/alter_data_set_additional_secret_added_error.log create mode 100644 tests/common/common_log_samples/alter_data_set_additional_secret_added_success.log create mode 100644 tests/common/common_log_samples/alter_group_additional_secret_added_error.log create mode 100644 tests/common/common_log_samples/alter_group_additional_secret_added_success.log create mode 100644 tests/common/common_log_samples/alter_resource_additional_secret_added_error.log create mode 100644 tests/common/common_log_samples/alter_resource_additional_secret_added_success.log diff --git a/tests/common/common_log_samples/alter_data_set_additional_secret_added_error.log b/tests/common/common_log_samples/alter_data_set_additional_secret_added_error.log new file mode 100644 index 00000000..9900478a --- /dev/null +++ b/tests/common/common_log_samples/alter_data_set_additional_secret_added_error.log @@ -0,0 +1,268 @@ + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470') + INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470 + + LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE + ----- -------- ---------------- ------- ----- + 00 ESWIFT READ NO NO + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + -------- + NO USER TO BE NOTIFIED + + YOUR ACCESS CREATION GROUP DATASET TYPE + ----------- -------------- ------------ + ALTER SYS1 NON-VSAM + + VOLUMES ON WHICH DATASET RESIDES + -------------------------------- + USRAT2 + + NO INSTALLATION DATA + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "messages": [ + "INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470", + null, + "LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE", + "----- -------- ---------------- ------- -----", + " 00 ESWIFT READ NO NO", + null, + "AUDITING", + "--------", + "FAILURES(READ)", + null, + "NOTIFY", + "--------", + "NO USER TO BE NOTIFIED", + null, + "YOUR ACCESS CREATION GROUP DATASET TYPE", + "----------- -------------- ------------", + " ALTER SYS1 NON-VSAM", + null, + "VOLUMES ON WHICH DATASET RESIDES", + "--------------------------------", + "USRAT2", + null, + "NO INSTALLATION DATA" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "profiles": [ + { + "base": { + "name": "eswift.test.t1136242.p3020470", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "warning": null, + "erase": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "yourAccess": "alter", + "creationGroup": "sys1", + "dataSetType": "non-vsam", + "volumes": [ + "usrat2" + ], + "installationData": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "********", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + ******** + eswift + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 8 + 16 + 4 + ADDSD ('ESWIFT.TEST.T113622.P3020470') + ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG + + + 8 + 16 + 4 + ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (********) + ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T113622.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ADDSD ('ESWIFT.TEST.T113622.P3020470')", + "messages": [ + "ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG" + ] + }, + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (********)", + "messages": [ + "ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_log_samples/alter_data_set_additional_secret_added_success.log b/tests/common/common_log_samples/alter_data_set_additional_secret_added_success.log new file mode 100644 index 00000000..af803124 --- /dev/null +++ b/tests/common/common_log_samples/alter_data_set_additional_secret_added_success.log @@ -0,0 +1,252 @@ + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470') + INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470 + + LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE + ----- -------- ---------------- ------- ----- + 00 ESWIFT READ NO NO + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + -------- + NO USER TO BE NOTIFIED + + YOUR ACCESS CREATION GROUP DATASET TYPE + ----------- -------------- ------------ + ALTER SYS1 NON-VSAM + + VOLUMES ON WHICH DATASET RESIDES + -------------------------------- + USRAT2 + + NO INSTALLATION DATA + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "messages": [ + "INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470", + null, + "LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE", + "----- -------- ---------------- ------- -----", + " 00 ESWIFT READ NO NO", + null, + "AUDITING", + "--------", + "FAILURES(READ)", + null, + "NOTIFY", + "--------", + "NO USER TO BE NOTIFIED", + null, + "YOUR ACCESS CREATION GROUP DATASET TYPE", + "----------- -------------- ------------", + " ALTER SYS1 NON-VSAM", + null, + "VOLUMES ON WHICH DATASET RESIDES", + "--------------------------------", + "USRAT2", + null, + "NO INSTALLATION DATA" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "profiles": [ + { + "base": { + "name": "eswift.test.t1136242.p3020470", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "warning": null, + "erase": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "yourAccess": "alter", + "creationGroup": "sys1", + "dataSetType": "non-vsam", + "volumes": [ + "usrat2" + ], + "installationData": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "Read", + "operation": null + }, + "base:owner": { + "value": "********", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + Read + ******** + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (********) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (********)" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_log_samples/alter_group_additional_secret_added_error.log b/tests/common/common_log_samples/alter_group_additional_secret_added_error.log new file mode 100644 index 00000000..1a5a033f --- /dev/null +++ b/tests/common/common_log_samples/alter_group_additional_secret_added_error.log @@ -0,0 +1,246 @@ + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTGRP TESTGRP0 + INFORMATION FOR GROUP TESTGRP0 + SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + NO SUBGROUPS + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + ESWIFT USE 000000 NONE + CONNECT ATTRIBUTES=SPECIAL + REVOKE DATE=NONE RESUME DATE=NONE + LEONARD USE 000000 NONE + CONNECT ATTRIBUTES=OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "messages": [ + "INFORMATION FOR GROUP TESTGRP0", + " SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150", + " NO INSTALLATION DATA", + " NO MODEL DATA SET", + " TERMUACC", + " NO SUBGROUPS", + " USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=", + " ESWIFT USE 000000 NONE", + " CONNECT ATTRIBUTES=SPECIAL", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LEONARD USE 000000 NONE", + " CONNECT ATTRIBUTES=OPERATIONS", + " REVOKE DATE=NONE RESUME DATE=NONE" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "profiles": [ + { + "base": { + "name": "testgrp0", + "superiorGroup": "sys1", + "owner": "eswift", + "created": "5/30/2023", + "installationData": null, + "modelDataSet": null, + "terminalUniversalAccess": true, + "subgroups": [], + "users": [ + { + "userid": "eswift", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "special" + ], + "revokeDate": null, + "resumeDate": null + }, + { + "userid": "leonard", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "operations" + ], + "revokeDate": null, + "resumeDate": null + } + ] + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{ + "omvs": { + "omvs:gid": { + "value": "********", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + ******** + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 8 + 16 + 8 + ALTGROUP TESTGRP0 OMVS (GID (********)) + REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "ALTGROUP TESTGRP0 OMVS (GID (********))", + "messages": [ + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING GID, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_log_samples/alter_group_additional_secret_added_success.log b/tests/common/common_log_samples/alter_group_additional_secret_added_success.log new file mode 100644 index 00000000..16ef1aa4 --- /dev/null +++ b/tests/common/common_log_samples/alter_group_additional_secret_added_success.log @@ -0,0 +1,238 @@ + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTGRP TESTGRP0 + INFORMATION FOR GROUP TESTGRP0 + SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + NO SUBGROUPS + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + ESWIFT USE 000000 NONE + CONNECT ATTRIBUTES=SPECIAL + REVOKE DATE=NONE RESUME DATE=NONE + LEONARD USE 000000 NONE + CONNECT ATTRIBUTES=OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "messages": [ + "INFORMATION FOR GROUP TESTGRP0", + " SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150", + " NO INSTALLATION DATA", + " NO MODEL DATA SET", + " TERMUACC", + " NO SUBGROUPS", + " USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=", + " ESWIFT USE 000000 NONE", + " CONNECT ATTRIBUTES=SPECIAL", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LEONARD USE 000000 NONE", + " CONNECT ATTRIBUTES=OPERATIONS", + " REVOKE DATE=NONE RESUME DATE=NONE" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "profiles": [ + { + "base": { + "name": "testgrp0", + "superiorGroup": "sys1", + "owner": "eswift", + "created": "5/30/2023", + "installationData": null, + "modelDataSet": null, + "terminalUniversalAccess": true, + "subgroups": [], + "users": [ + { + "userid": "eswift", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "special" + ], + "revokeDate": null, + "resumeDate": null + }, + { + "userid": "leonard", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "operations" + ], + "revokeDate": null, + "resumeDate": null + } + ] + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{ + "omvs": { + "omvs:gid": { + "value": "********", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + ******** + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTGROUP TESTGRP0 OMVS (GID (********)) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "testgrp0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTGROUP TESTGRP0 OMVS (GID (********))" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_log_samples/alter_resource_additional_secret_added_error.log b/tests/common/common_log_samples/alter_resource_additional_secret_added_error.log new file mode 100644 index 00000000..c371a5ea --- /dev/null +++ b/tests/common/common_log_samples/alter_resource_additional_secret_added_error.log @@ -0,0 +1,257 @@ + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + + 0 + 0 + 0 + RLIST ELIJTEST (TESTING) + CLASS NAME + ----- ---- + ELIJTEST TESTING + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ READ NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + SUCCESS(UPDATE),FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "messages": [ + "CLASS NAME", + "----- ----", + "ELIJTEST TESTING", + " ", + "LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING", + "----- -------- ---------------- ----------- -------", + " 00 ESWIFT READ READ NO", + " ", + "INSTALLATION DATA", + "-----------------", + "NONE", + " ", + "APPLICATION DATA", + "----------------", + "NONE", + " ", + "AUDITING", + "--------", + "SUCCESS(UPDATE),FAILURES(READ)", + " ", + "NOTIFY", + "------", + "NO USER TO BE NOTIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "profiles": [ + { + "base": { + "class": "elijtest", + "name": "testing", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "yourAccess": "read", + "warning": null, + "installationData": null, + "applicationData": null, + "auditing": { + "success": "update", + "failures": "read" + }, + "notify": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "********", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + ******** + eswift + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 8 + 16 + 8 + RALTER ELIJTEST (TESTING) UACC (********) OWNER (eswift) + REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION + REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION + IKJ56701I MISSING ALTER, CONTROL, UPDATE, READ, EXECUTE, OR NONE + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "RALTER ELIJTEST (TESTING) UACC (********) OWNER (eswift)", + "messages": [ + "REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56702I FOR MORE INFORMATION", + "REDACTED MESSAGE CONCERNING UNIVERSAL ACCESS, REVIEW DOCUMENTATION OF IKJ56701I FOR MORE INFORMATION", + "IKJ56701I MISSING ALTER, CONTROL, UPDATE, READ, EXECUTE, OR NONE" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_log_samples/alter_resource_additional_secret_added_success.log b/tests/common/common_log_samples/alter_resource_additional_secret_added_success.log new file mode 100644 index 00000000..e86e04a5 --- /dev/null +++ b/tests/common/common_log_samples/alter_resource_additional_secret_added_success.log @@ -0,0 +1,253 @@ + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + + 0 + 0 + 0 + RLIST ELIJTEST (TESTING) + CLASS NAME + ----- ---- + ELIJTEST TESTING + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ READ NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + SUCCESS(UPDATE),FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "messages": [ + "CLASS NAME", + "----- ----", + "ELIJTEST TESTING", + " ", + "LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING", + "----- -------- ---------------- ----------- -------", + " 00 ESWIFT READ READ NO", + " ", + "INSTALLATION DATA", + "-----------------", + "NONE", + " ", + "APPLICATION DATA", + "----------------", + "NONE", + " ", + "AUDITING", + "--------", + "SUCCESS(UPDATE),FAILURES(READ)", + " ", + "NOTIFY", + "------", + "NO USER TO BE NOTIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "profiles": [ + { + "base": { + "class": "elijtest", + "name": "testing", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "yourAccess": "read", + "warning": null, + "installationData": null, + "applicationData": null, + "auditing": { + "success": "update", + "failures": "read" + }, + "notify": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "Read", + "operation": null + }, + "base:owner": { + "value": "********", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + Read + ******** + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + RALTER ELIJTEST (TESTING) UACC (Read) OWNER (********) + ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED. + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RALTER ELIJTEST (TESTING) UACC (Read) OWNER (********)", + "messages": [ + "ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/common/common_result_samples/alter_group_result_error_gid_secret.json b/tests/common/common_result_samples/alter_group_result_error_gid_secret.json index d201e1d8..33de23bd 100644 --- a/tests/common/common_result_samples/alter_group_result_error_gid_secret.json +++ b/tests/common/common_result_samples/alter_group_result_error_gid_secret.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/common/common_result_samples/alter_group_result_success_gid_secret.json b/tests/common/common_result_samples/alter_group_result_success_gid_secret.json index 40655569..98e53f28 100644 --- a/tests/common/common_result_samples/alter_group_result_success_gid_secret.json +++ b/tests/common/common_result_samples/alter_group_result_success_gid_secret.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/common/test_additional_secrets_redaction.py b/tests/common/test_additional_secrets_redaction.py index b142bdc8..4faa1a27 100644 --- a/tests/common/test_additional_secrets_redaction.py +++ b/tests/common/test_additional_secrets_redaction.py @@ -30,6 +30,7 @@ class TestAdditionalSecretsResultRedaction(unittest.TestCase): maxDiff = None + ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") # ============================================================================ # User Administration @@ -119,6 +120,61 @@ def test_user_admin_custom_secret_redacted_request(self): str(result), ) + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_success( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, + ) + self.assertNotIn( + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"]), + success_log, + ) + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_error( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + user_admin.alter( + "squidwrd", + traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, + ) + except SecurityRequestError: + pass + error_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + error_log, + TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG, + ) + self.assertNotIn( + str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"]), + error_log, + ) + # ============================================================================ # Group Administration # ============================================================================ @@ -182,6 +238,61 @@ def test_group_admin_custom_secret_redacted_request(self): str(result), ) + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_group_request_debug_log_additional_secret_added_get_redacted_on_success( + self, + call_racf_mock: Mock, + ): + group_admin = GroupAdmin(debug=True, additional_secret_traits=["omvs:gid"]) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + group_admin.alter( + "testgrp0", + traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_ALTER_GROUP_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, + ) + self.assertNotIn( + str(TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS["omvs:gid"]), + success_log, + ) + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_group_request_debug_log_additional_secret_added_get_redacted_on_error( + self, + call_racf_mock: Mock, + ): + group_admin = GroupAdmin(debug=True, additional_secret_traits=["omvs:gid"]) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + group_admin.alter( + "testgrp0", + traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, + ) + except SecurityRequestError: + pass + error_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + error_log, + TestCommonConstants.TEST_ALTER_GROUP_ADDITIONAL_SECRET_ADDED_ERROR_LOG, + ) + self.assertNotIn( + str(TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS["omvs:gid"]), + error_log, + ) + # ============================================================================ # General Resource Profile Administration # ============================================================================ @@ -277,6 +388,62 @@ def test_resource_admin_custom_secret_redacted_request(self): str(result), ) + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_resource_request_debug_log_additional_secret_added_get_redacted_on_success( + self, + call_racf_mock: Mock, + ): + resource_admin = ResourceAdmin( + debug=True, additional_secret_traits=["base:owner"] + ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + resource_admin.alter( + "TESTING", + "ELIJTEST", + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_ALTER_RESOURCE_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, + ) + # No AssertNotInTest because Equal test to log is sufficient and value technically appears + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_resource_request_debug_log_additional_secret_added_get_redacted_on_error( + self, + call_racf_mock: Mock, + ): + secret_trait = "base:universal_access" + resource_admin = ResourceAdmin( + debug=True, additional_secret_traits=[secret_trait] + ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + resource_admin.alter( + "TESTING", + "ELIJTEST", + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS, + ) + except SecurityRequestError: + pass + error_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + error_log, + TestCommonConstants.TEST_ALTER_RESOURCE_ADDITIONAL_SECRET_ADDED_ERROR_LOG, + ) + # No AssertNotInTest because Equal test to log is sufficient and value technically appears + # ============================================================================ # Data Set Profile Administration # ============================================================================ @@ -341,6 +508,63 @@ def test_data_set_admin_custom_secret_redacted_request(self): str(result), ) + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_data_set_request_debug_log_additional_secret_added_get_redacted_on_success( + self, + call_racf_mock: Mock, + ): + data_set_admin = DataSetAdmin( + debug=True, additional_secret_traits=["base:owner"] + ) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + data_set_admin.alter( + "ESWIFT.TEST.T1136242.P3020470", + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_ALTER_DATA_SET_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, + ) + # No AssertNotInTest because Equal test to log is sufficient and value technically appears + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_alter_data_set_request_debug_log_additional_secret_added_get_redacted_on_error( + self, + call_racf_mock: Mock, + ): + secret_trait = "base:universal_access" + data_set_admin = DataSetAdmin( + debug=True, additional_secret_traits=[secret_trait] + ) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + try: + data_set_admin.alter( + "ESWIFT.TEST.T1136242.P3020470", + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_ERROR_TRAITS, + ) + except SecurityRequestError: + pass + error_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + error_log, + TestCommonConstants.TEST_ALTER_DATA_SET_ADDITIONAL_SECRET_ADDED_ERROR_LOG, + ) + # No AssertNotInTest because Equal test to log is sufficient and value technically appears + + # ============================================================================ + # Generic Testing for all Admin Types + # ============================================================================ def test_incompatible_admin_custom_secret_redaction_error(self): admin_types = [ (AccessAdmin, "permission"), @@ -382,68 +606,3 @@ def test_compatible_admin_incompatible_traits_custom_secret_redaction_error(self f"Cannot add specified additional secrets to '{profile_type}' object.\n" + incompatible_traits_text, ) - - -@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") -class TestAdditionalSecretsLoggingRedaction(unittest.TestCase): - maxDiff = None - user_admin = UserAdmin(debug=True) - ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") - - # ============================================================================ - # Add Additional Secrets - # ============================================================================ - def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_success( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_EXTENDED_SUCCESS_XML, - ] - stdout = io.StringIO() - with contextlib.redirect_stdout(stdout): - user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED, - ) - success_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual( - success_log, - TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG, - ) - self.assertNotIn( - str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_EXTENDED["omvs:uid"]), - success_log, - ) - - # Secret redacted from command image but not from resulting error message. - # Marked experimental until resolved - def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_error( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin(debug=True, additional_secret_traits=["omvs:uid"]) - call_racf_mock.side_effect = [ - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, - TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, - ] - stdout = io.StringIO() - with contextlib.redirect_stdout(stdout): - try: - user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, - ) - except SecurityRequestError: - pass - error_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual( - error_log, - TestCommonConstants.TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG, - ) - self.assertNotIn( - str(TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR["omvs:uid"]), - error_log, - ) diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index c83f51a4..5ff149bc 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -110,6 +110,24 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "omvs:default_shell": "/bin/sh", "base:installation_data": "Test = Value; Other(stuff goes here)'')", } +TEST_ALTER_USER_RESULT_SUCCESS_UID_SECRET_DICTIONARY = get_sample( + "alter_user_result_success_uid_secret.json" +) +TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY = get_sample( + "alter_user_result_error_uid_secret.json" +) +TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML = get_sample( + "alter_user_result_success_inst_data.xml" +) +TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY = get_sample( + "alter_user_result_success_inst_data_secret.json" +) +TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( + "alter_user_additional_secret_added_success.log" +) +TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( + "alter_user_additional_secret_added_error.log" +) TEST_ALTER_DATA_SET_RESULT_SUCCESS_OWNER_SECRET_DICTIONARY = get_sample( "alter_data_set_result_success_owner_secret.json" @@ -117,6 +135,12 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_DATA_SET_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( "alter_data_set_result_error_uacc_secret.json" ) +TEST_ALTER_DATA_SET_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( + "alter_data_set_additional_secret_added_success.log" +) +TEST_ALTER_DATA_SET_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( + "alter_data_set_additional_secret_added_error.log" +) TEST_ALTER_GROUP_RESULT_SUCCESS_GID_SECRET_DICTIONARY = get_sample( "alter_group_result_success_gid_secret.json" @@ -124,6 +148,12 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_GROUP_RESULT_ERROR_GID_SECRET_DICTIONARY = get_sample( "alter_group_result_error_gid_secret.json" ) +TEST_ALTER_GROUP_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( + "alter_group_additional_secret_added_success.log" +) +TEST_ALTER_GROUP_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( + "alter_group_additional_secret_added_error.log" +) TEST_ALTER_RESOURCE_OVERWRITE_AUDIT_RESULT_SUCCESS_XML = get_sample( "alter_resource_overwrite_audit_result_success.xml" @@ -137,25 +167,11 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_ALTER_RESOURCE_RESULT_ERROR_UACC_SECRET_DICTIONARY = get_sample( "alter_resource_result_error_uacc_secret.json" ) - -TEST_ALTER_USER_RESULT_SUCCESS_UID_SECRET_DICTIONARY = get_sample( - "alter_user_result_success_uid_secret.json" -) -TEST_ALTER_USER_RESULT_ERROR_UID_SECRET_DICTIONARY = get_sample( - "alter_user_result_error_uid_secret.json" -) -TEST_ALTER_USER_RESULT_INST_DATA_SUCCESS_XML = get_sample( - "alter_user_result_success_inst_data.xml" -) -TEST_ALTER_USER_RESULT_SUCCESS_INST_DATA_SECRET_DICTIONARY = get_sample( - "alter_user_result_success_inst_data_secret.json" -) - -TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( - "alter_user_additional_secret_added_success.log" +TEST_ALTER_RESOURCE_ADDITIONAL_SECRET_ADDED_SUCCESS_LOG = get_sample( + "alter_resource_additional_secret_added_success.log" ) -TEST_ALTER_USER_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( - "alter_user_additional_secret_added_error.log" +TEST_ALTER_RESOURCE_ADDITIONAL_SECRET_ADDED_ERROR_LOG = get_sample( + "alter_resource_additional_secret_added_error.log" ) # ============================================================================ diff --git a/tests/connection/connection_log_samples/connect_connection_error.log b/tests/connection/connection_log_samples/connect_connection_error.log index 1563c4bc..74d05a20 100644 --- a/tests/connection/connection_log_samples/connect_connection_error.log +++ b/tests/connection/connection_log_samples/connect_connection_error.log @@ -24,7 +24,7 @@ - + @@ -38,7 +38,7 @@ - + 8 16 @@ -62,7 +62,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "set", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_log_samples/connect_connection_success.log b/tests/connection/connection_log_samples/connect_connection_success.log index c10b7e01..215b104a 100644 --- a/tests/connection/connection_log_samples/connect_connection_success.log +++ b/tests/connection/connection_log_samples/connect_connection_success.log @@ -24,7 +24,7 @@ - + @@ -38,7 +38,7 @@ - + 0 0 @@ -60,7 +60,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "set", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_request_samples/connect_connection_request.xml b/tests/connection/connection_request_samples/connect_connection_request.xml index f6c274d1..d7eda207 100644 --- a/tests/connection/connection_request_samples/connect_connection_request.xml +++ b/tests/connection/connection_request_samples/connect_connection_request.xml @@ -1,5 +1,5 @@ - + diff --git a/tests/connection/connection_request_samples/connection_give_group_access_attribute.xml b/tests/connection/connection_request_samples/connection_give_group_access_attribute.xml index e5d15a33..f37d69c9 100644 --- a/tests/connection/connection_request_samples/connection_give_group_access_attribute.xml +++ b/tests/connection/connection_request_samples/connection_give_group_access_attribute.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_give_group_auditor_authority.xml b/tests/connection/connection_request_samples/connection_give_group_auditor_authority.xml index 8bb8b7af..9c0a48c0 100644 --- a/tests/connection/connection_request_samples/connection_give_group_auditor_authority.xml +++ b/tests/connection/connection_request_samples/connection_give_group_auditor_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_give_group_operations_authority.xml b/tests/connection/connection_request_samples/connection_give_group_operations_authority.xml index cfaf13fa..b2b01bb5 100644 --- a/tests/connection/connection_request_samples/connection_give_group_operations_authority.xml +++ b/tests/connection/connection_request_samples/connection_give_group_operations_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_give_group_special_authority.xml b/tests/connection/connection_request_samples/connection_give_group_special_authority.xml index b39b16c9..4b4d05eb 100644 --- a/tests/connection/connection_request_samples/connection_give_group_special_authority.xml +++ b/tests/connection/connection_request_samples/connection_give_group_special_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_take_away_group_access_attribute.xml b/tests/connection/connection_request_samples/connection_take_away_group_access_attribute.xml index 622e3808..0af86fe9 100644 --- a/tests/connection/connection_request_samples/connection_take_away_group_access_attribute.xml +++ b/tests/connection/connection_request_samples/connection_take_away_group_access_attribute.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_take_away_group_auditor_authority.xml b/tests/connection/connection_request_samples/connection_take_away_group_auditor_authority.xml index a5d35c65..17cee5c5 100644 --- a/tests/connection/connection_request_samples/connection_take_away_group_auditor_authority.xml +++ b/tests/connection/connection_request_samples/connection_take_away_group_auditor_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_take_away_group_operations_authority.xml b/tests/connection/connection_request_samples/connection_take_away_group_operations_authority.xml index de82369e..99380b80 100644 --- a/tests/connection/connection_request_samples/connection_take_away_group_operations_authority.xml +++ b/tests/connection/connection_request_samples/connection_take_away_group_operations_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/connection_take_away_group_special_authority.xml b/tests/connection/connection_request_samples/connection_take_away_group_special_authority.xml index 330c1e12..c7b3c7d9 100644 --- a/tests/connection/connection_request_samples/connection_take_away_group_special_authority.xml +++ b/tests/connection/connection_request_samples/connection_take_away_group_special_authority.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_request_samples/delete_connection_request.xml b/tests/connection/connection_request_samples/delete_connection_request.xml index 94e897e7..97ccba22 100644 --- a/tests/connection/connection_request_samples/delete_connection_request.xml +++ b/tests/connection/connection_request_samples/delete_connection_request.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/tests/connection/connection_result_samples/connect_connection_result_error.json b/tests/connection/connection_result_samples/connect_connection_result_error.json index fe180153..ada4d6f9 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_error.json +++ b/tests/connection/connection_result_samples/connect_connection_result_error.json @@ -2,7 +2,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "set", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_result_samples/connect_connection_result_error.xml b/tests/connection/connection_result_samples/connect_connection_result_error.xml index 4c16be89..30f46941 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_error.xml +++ b/tests/connection/connection_result_samples/connect_connection_result_error.xml @@ -1,6 +1,6 @@ - + 8 16 diff --git a/tests/connection/connection_result_samples/connect_connection_result_success.json b/tests/connection/connection_result_samples/connect_connection_result_success.json index 75d10ecc..62a670fc 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_success.json +++ b/tests/connection/connection_result_samples/connect_connection_result_success.json @@ -2,7 +2,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "set", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_result_samples/connect_connection_result_success.xml b/tests/connection/connection_result_samples/connect_connection_result_success.xml index 82e960db..786d994a 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_success.xml +++ b/tests/connection/connection_result_samples/connect_connection_result_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/connection/connection_result_samples/delete_connection_result_error.json b/tests/connection/connection_result_samples/delete_connection_result_error.json index 193ad0f6..6b938059 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_error.json +++ b/tests/connection/connection_result_samples/delete_connection_result_error.json @@ -2,7 +2,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "del", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_result_samples/delete_connection_result_error.xml b/tests/connection/connection_result_samples/delete_connection_result_error.xml index f6ba65ef..fee101db 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_error.xml +++ b/tests/connection/connection_result_samples/delete_connection_result_error.xml @@ -1,6 +1,6 @@ - + 8 16 diff --git a/tests/connection/connection_result_samples/delete_connection_result_success.json b/tests/connection/connection_result_samples/delete_connection_result_success.json index 0b4a3d20..f9f29a19 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_success.json +++ b/tests/connection/connection_result_samples/delete_connection_result_success.json @@ -2,7 +2,7 @@ "securityResult": { "groupConnection": { "name": "ESWIFT", - "group": "TESTGRP0", + "group": "testgrp0", "operation": "del", "requestId": "ConnectionRequest", "commands": [ diff --git a/tests/connection/connection_result_samples/delete_connection_result_success.xml b/tests/connection/connection_result_samples/delete_connection_result_success.xml index 38174748..7b4eb078 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_success.xml +++ b/tests/connection/connection_result_samples/delete_connection_result_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/connection/test_connection_debug_logging.py b/tests/connection/test_connection_debug_logging.py index ea9b80c1..fbfc0a33 100644 --- a/tests/connection/test_connection_debug_logging.py +++ b/tests/connection/test_connection_debug_logging.py @@ -35,7 +35,7 @@ def test_connect_connection_request_debug_log_works_on_success( with contextlib.redirect_stdout(stdout): self.connection_admin.connect( "ESWIFT", - "TESTGRP0", + "testgrp0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ), success_log = self.ansi_escape.sub("", stdout.getvalue()) @@ -55,7 +55,7 @@ def test_connect_connection_request_debug_log_works_on_error( try: self.connection_admin.connect( "ESWIFT", - "TESTGRP0", + "testgrp0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ), except SecurityRequestError: diff --git a/tests/connection/test_connection_request_builder.py b/tests/connection/test_connection_request_builder.py index 620bf1de..a80e2ba7 100644 --- a/tests/connection/test_connection_request_builder.py +++ b/tests/connection/test_connection_request_builder.py @@ -18,7 +18,7 @@ class TestConnectionRequestBuilder(unittest.TestCase): def test_connection_admin_build_connect_connection_request(self): result = self.connection_admin.connect( "ESWIFT", - "TESTGRP0", + "testgrp0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ) self.assertEqual( @@ -26,7 +26,7 @@ def test_connection_admin_build_connect_connection_request(self): ) def test_connection_admin_build_delete_connection_request(self): - result = self.connection_admin.delete("ESWIFT", "TESTGRP0") + result = self.connection_admin.delete("ESWIFT", "testgrp0") self.assertEqual( result, TestConnectionConstants.TEST_DELETE_CONNECTION_REQUEST_XML ) diff --git a/tests/connection/test_connection_result_parser.py b/tests/connection/test_connection_result_parser.py index 0df0e9f8..dd21d1b1 100644 --- a/tests/connection/test_connection_result_parser.py +++ b/tests/connection/test_connection_result_parser.py @@ -30,7 +30,7 @@ def test_connection_admin_can_parse_connect_connection_success_xml( self.assertEqual( self.connection_admin.connect( "ESWIFT", - "TESTGRP0", + "testgrp0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ), TestConnectionConstants.TEST_CONNECT_CONNECTION_RESULT_SUCCESS_DICTIONARY, @@ -47,7 +47,7 @@ def test_connection_admin_can_parse_connect_connection_error_xml( with self.assertRaises(SecurityRequestError) as exception: self.connection_admin.connect( "ESWIFT", - "TESTGRP0", + "testgrp0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ) self.assertEqual( @@ -66,7 +66,7 @@ def test_connection_admin_can_parse_delete_connection_success_xml( TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_SUCCESS_XML ) self.assertEqual( - self.connection_admin.delete("ESWIFT", "TESTGRP0"), + self.connection_admin.delete("ESWIFT", "testgrp0"), TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_SUCCESS_DICTIONARY, ) @@ -79,7 +79,7 @@ def test_connection_admin_can_parse_delete_connection_error_xml( TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_ERROR_XML ) with self.assertRaises(SecurityRequestError) as exception: - self.connection_admin.delete("ESWIFT", "TESTGRP0") + self.connection_admin.delete("ESWIFT", "testgrp0") self.assertEqual( exception.exception.result, TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_ERROR_DICTIONARY, diff --git a/tests/connection/test_connection_setters.py b/tests/connection/test_connection_setters.py index 37d91099..20e1327c 100644 --- a/tests/connection/test_connection_setters.py +++ b/tests/connection/test_connection_setters.py @@ -20,7 +20,7 @@ class TestConnectionSetters(unittest.TestCase): # ============================================================================ def test_connection_admin_build_give_group_special_request(self): result = self.connection_admin.give_group_special_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, TestConnectionConstants.TEST_CONNECTION_GIVE_GROUP_SPECIAL_AUTHORITY @@ -28,7 +28,7 @@ def test_connection_admin_build_give_group_special_request(self): def test_connection_admin_build_take_away_group_special_authority_request(self): result = self.connection_admin.take_away_group_special_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, @@ -40,7 +40,7 @@ def test_connection_admin_build_take_away_group_special_authority_request(self): # ============================================================================ def test_connection_admin_build_give_group_auditor_authority_request(self): result = self.connection_admin.give_group_auditor_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, TestConnectionConstants.TEST_CONNECTION_GIVE_GROUP_AUDITOR_AUTHORITY @@ -48,7 +48,7 @@ def test_connection_admin_build_give_group_auditor_authority_request(self): def test_connection_admin_build_take_away_group_auditor_authority_request(self): result = self.connection_admin.take_away_group_auditor_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, @@ -60,7 +60,7 @@ def test_connection_admin_build_take_away_group_auditor_authority_request(self): # ============================================================================ def test_connection_admin_build_give_group_operations_authority_request(self): result = self.connection_admin.give_group_operations_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, @@ -69,7 +69,7 @@ def test_connection_admin_build_give_group_operations_authority_request(self): def test_connection_admin_build_take_away_group_operations_authority_request(self): result = self.connection_admin.take_away_group_operations_authority( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, @@ -80,14 +80,14 @@ def test_connection_admin_build_take_away_group_operations_authority_request(sel # Group Access Attribute # ============================================================================ def test_connection_admin_build_give_group_access_attribute_request(self): - result = self.connection_admin.give_group_access_attribute("ESWIFT", "TESTGRP0") + result = self.connection_admin.give_group_access_attribute("ESWIFT", "testgrp0") self.assertEqual( result, TestConnectionConstants.TEST_CONNECTION_SET_GROUP_ACCESS_ATTRIBUTE ) def test_connection_admin_build_take_away_group_access_attribute(self): result = self.connection_admin.take_away_group_access_attribute( - "ESWIFT", "TESTGRP0" + "ESWIFT", "testgrp0" ) self.assertEqual( result, diff --git a/tests/data_set/data_set_log_samples/alter_data_set_error.log b/tests/data_set/data_set_log_samples/alter_data_set_error.log index 90cd31b1..5371bdf1 100644 --- a/tests/data_set/data_set_log_samples/alter_data_set_error.log +++ b/tests/data_set/data_set_log_samples/alter_data_set_error.log @@ -173,7 +173,7 @@ { "base": { "base:universal_access": { - "value": "Read", + "value": "Alter", "operation": null }, "base:owner": { @@ -192,7 +192,7 @@ - Read + Alter eswift diff --git a/tests/data_set/test_data_set_constants.py b/tests/data_set/test_data_set_constants.py index cf050ab2..d9633d22 100644 --- a/tests/data_set/test_data_set_constants.py +++ b/tests/data_set/test_data_set_constants.py @@ -97,6 +97,10 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "base:universal_access": "Read", "base:owner": "eswift", } +TEST_ALTER_DATA_SET_REQUEST_ERROR_TRAITS = { + "base:universal_access": "Alter", + "base:owner": "eswift", +} # Extract Data Set TEST_EXTRACT_DATA_SET_REQUEST_BASE_XML = get_sample("extract_data_set_request_base.xml") diff --git a/tests/data_set/test_data_set_debug_logging.py b/tests/data_set/test_data_set_debug_logging.py index 8edd78d0..08963ccc 100644 --- a/tests/data_set/test_data_set_debug_logging.py +++ b/tests/data_set/test_data_set_debug_logging.py @@ -56,7 +56,7 @@ def test_alter_data_set_request_debug_log_works_on_error( try: self.data_set_admin.alter( "ESWIFT.TEST.T1136242.P3020470", - traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_ERROR_TRAITS, ) except SecurityRequestError: pass diff --git a/tests/group/group_log_samples/alter_group_error.log b/tests/group/group_log_samples/alter_group_error.log index 34205d68..6299214b 100644 --- a/tests/group/group_log_samples/alter_group_error.log +++ b/tests/group/group_log_samples/alter_group_error.log @@ -13,7 +13,7 @@ - + @@ -24,7 +24,7 @@ - + 0 0 @@ -58,7 +58,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ @@ -100,7 +100,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ @@ -178,7 +178,7 @@ - + 3000000000 @@ -193,7 +193,7 @@ - + Definition exists. Add command skipped due to precheck option 8 @@ -218,7 +218,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/group/group_log_samples/alter_group_success.log b/tests/group/group_log_samples/alter_group_success.log index 7bf5e943..79e35607 100644 --- a/tests/group/group_log_samples/alter_group_success.log +++ b/tests/group/group_log_samples/alter_group_success.log @@ -13,7 +13,7 @@ - + @@ -24,7 +24,7 @@ - + 0 0 @@ -58,7 +58,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ @@ -100,7 +100,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ @@ -178,7 +178,7 @@ - + 1234567 @@ -193,7 +193,7 @@ - + Definition exists. Add command skipped due to precheck option 0 @@ -215,7 +215,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/group/group_log_samples/extract_group_base_omvs_error.log b/tests/group/group_log_samples/extract_group_base_omvs_error.log index cb969528..fe0e48ba 100644 --- a/tests/group/group_log_samples/extract_group_base_omvs_error.log +++ b/tests/group/group_log_samples/extract_group_base_omvs_error.log @@ -15,7 +15,7 @@ - + @@ -28,7 +28,7 @@ - + 8 16 @@ -50,7 +50,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_log_samples/extract_group_base_omvs_success.log b/tests/group/group_log_samples/extract_group_base_omvs_success.log index fcf3adde..9ac440b1 100644 --- a/tests/group/group_log_samples/extract_group_base_omvs_success.log +++ b/tests/group/group_log_samples/extract_group_base_omvs_success.log @@ -15,7 +15,7 @@ - + @@ -28,7 +28,7 @@ - + 0 0 @@ -60,7 +60,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ @@ -100,7 +100,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_request_samples/add_group_request.xml b/tests/group/group_request_samples/add_group_request.xml index e4967aea..0efb989f 100644 --- a/tests/group/group_request_samples/add_group_request.xml +++ b/tests/group/group_request_samples/add_group_request.xml @@ -1,5 +1,5 @@ - + 6667 diff --git a/tests/group/group_request_samples/alter_group_request.xml b/tests/group/group_request_samples/alter_group_request.xml index ee9ba6d6..d2e10601 100644 --- a/tests/group/group_request_samples/alter_group_request.xml +++ b/tests/group/group_request_samples/alter_group_request.xml @@ -1,5 +1,5 @@ - + 1234567 diff --git a/tests/group/group_request_samples/delete_group_request.xml b/tests/group/group_request_samples/delete_group_request.xml index f68558b0..007f9ddf 100644 --- a/tests/group/group_request_samples/delete_group_request.xml +++ b/tests/group/group_request_samples/delete_group_request.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/tests/group/group_request_samples/extract_group_request_base_omvs.xml b/tests/group/group_request_samples/extract_group_request_base_omvs.xml index 75fb3c6e..4538a757 100644 --- a/tests/group/group_request_samples/extract_group_request_base_omvs.xml +++ b/tests/group/group_request_samples/extract_group_request_base_omvs.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/tests/group/group_request_samples/group_set_omvs_gid.xml b/tests/group/group_request_samples/group_set_omvs_gid.xml index ee9ba6d6..d2e10601 100644 --- a/tests/group/group_request_samples/group_set_omvs_gid.xml +++ b/tests/group/group_request_samples/group_set_omvs_gid.xml @@ -1,5 +1,5 @@ - + 1234567 diff --git a/tests/group/group_request_samples/group_set_ovm_gid.xml b/tests/group/group_request_samples/group_set_ovm_gid.xml index 5c9acb29..93220ef3 100644 --- a/tests/group/group_request_samples/group_set_ovm_gid.xml +++ b/tests/group/group_request_samples/group_set_ovm_gid.xml @@ -1,5 +1,5 @@ - + 1234567 diff --git a/tests/group/group_result_samples/add_group_result_success.json b/tests/group/group_result_samples/add_group_result_success.json index 1ae3e4bb..1a1fc5e3 100644 --- a/tests/group/group_result_samples/add_group_result_success.json +++ b/tests/group/group_result_samples/add_group_result_success.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/add_group_result_success.xml b/tests/group/group_result_samples/add_group_result_success.xml index 98461f6f..6fa8b5c4 100644 --- a/tests/group/group_result_samples/add_group_result_success.xml +++ b/tests/group/group_result_samples/add_group_result_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/group/group_result_samples/alter_group_result_error.json b/tests/group/group_result_samples/alter_group_result_error.json index 77d89a27..4a1f7860 100644 --- a/tests/group/group_result_samples/alter_group_result_error.json +++ b/tests/group/group_result_samples/alter_group_result_error.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/group/group_result_samples/alter_group_result_error.xml b/tests/group/group_result_samples/alter_group_result_error.xml index 4b0617fa..da49c5ef 100644 --- a/tests/group/group_result_samples/alter_group_result_error.xml +++ b/tests/group/group_result_samples/alter_group_result_error.xml @@ -1,6 +1,6 @@ - + Definition exists. Add command skipped due to precheck option 8 diff --git a/tests/group/group_result_samples/alter_group_result_success.json b/tests/group/group_result_samples/alter_group_result_success.json index 19a411f6..8bb226bd 100644 --- a/tests/group/group_result_samples/alter_group_result_success.json +++ b/tests/group/group_result_samples/alter_group_result_success.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "set", "requestId": "GroupRequest", "info": [ diff --git a/tests/group/group_result_samples/alter_group_result_success.xml b/tests/group/group_result_samples/alter_group_result_success.xml index 57a90029..dedb13bc 100644 --- a/tests/group/group_result_samples/alter_group_result_success.xml +++ b/tests/group/group_result_samples/alter_group_result_success.xml @@ -1,6 +1,6 @@ - + Definition exists. Add command skipped due to precheck option 0 diff --git a/tests/group/group_result_samples/delete_group_result_error.json b/tests/group/group_result_samples/delete_group_result_error.json index afefddae..04e2aa62 100644 --- a/tests/group/group_result_samples/delete_group_result_error.json +++ b/tests/group/group_result_samples/delete_group_result_error.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "del", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/delete_group_result_error.xml b/tests/group/group_result_samples/delete_group_result_error.xml index 9691be79..5ee1e658 100644 --- a/tests/group/group_result_samples/delete_group_result_error.xml +++ b/tests/group/group_result_samples/delete_group_result_error.xml @@ -1,6 +1,6 @@ - + 8 16 diff --git a/tests/group/group_result_samples/delete_group_result_success.json b/tests/group/group_result_samples/delete_group_result_success.json index 13ff5a4e..757001b5 100644 --- a/tests/group/group_result_samples/delete_group_result_success.json +++ b/tests/group/group_result_samples/delete_group_result_success.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "del", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/delete_group_result_success.xml b/tests/group/group_result_samples/delete_group_result_success.xml index d87ac6db..8bc5cd05 100644 --- a/tests/group/group_result_samples/delete_group_result_success.xml +++ b/tests/group/group_result_samples/delete_group_result_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/group/group_result_samples/extract_group_result_base_omvs_error.json b/tests/group/group_result_samples/extract_group_result_base_omvs_error.json index 745d18f8..336c87fe 100644 --- a/tests/group/group_result_samples/extract_group_result_base_omvs_error.json +++ b/tests/group/group_result_samples/extract_group_result_base_omvs_error.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/extract_group_result_base_omvs_error.xml b/tests/group/group_result_samples/extract_group_result_base_omvs_error.xml index 599b7d67..8a66dcfa 100644 --- a/tests/group/group_result_samples/extract_group_result_base_omvs_error.xml +++ b/tests/group/group_result_samples/extract_group_result_base_omvs_error.xml @@ -1,6 +1,6 @@ - + 8 16 diff --git a/tests/group/group_result_samples/extract_group_result_base_omvs_success.json b/tests/group/group_result_samples/extract_group_result_base_omvs_success.json index 2b57fad5..987a9093 100644 --- a/tests/group/group_result_samples/extract_group_result_base_omvs_success.json +++ b/tests/group/group_result_samples/extract_group_result_base_omvs_success.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/extract_group_result_base_omvs_success.xml b/tests/group/group_result_samples/extract_group_result_base_omvs_success.xml index 36855b23..081516a6 100644 --- a/tests/group/group_result_samples/extract_group_result_base_omvs_success.xml +++ b/tests/group/group_result_samples/extract_group_result_base_omvs_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/group/group_result_samples/extract_group_result_base_only_error.json b/tests/group/group_result_samples/extract_group_result_base_only_error.json index 0881b1fb..6826720f 100644 --- a/tests/group/group_result_samples/extract_group_result_base_only_error.json +++ b/tests/group/group_result_samples/extract_group_result_base_only_error.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/extract_group_result_base_only_error.xml b/tests/group/group_result_samples/extract_group_result_base_only_error.xml index 9cef8fac..83403145 100644 --- a/tests/group/group_result_samples/extract_group_result_base_only_error.xml +++ b/tests/group/group_result_samples/extract_group_result_base_only_error.xml @@ -1,6 +1,6 @@ - + 8 16 diff --git a/tests/group/group_result_samples/extract_group_result_base_only_success.json b/tests/group/group_result_samples/extract_group_result_base_only_success.json index 8ae9b377..e6476730 100644 --- a/tests/group/group_result_samples/extract_group_result_base_only_success.json +++ b/tests/group/group_result_samples/extract_group_result_base_only_success.json @@ -1,7 +1,7 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "testgrp0", "operation": "listdata", "requestId": "GroupRequest", "commands": [ diff --git a/tests/group/group_result_samples/extract_group_result_base_only_success.xml b/tests/group/group_result_samples/extract_group_result_base_only_success.xml index 4986153a..904285fe 100644 --- a/tests/group/group_result_samples/extract_group_result_base_only_success.xml +++ b/tests/group/group_result_samples/extract_group_result_base_only_success.xml @@ -1,6 +1,6 @@ - + 0 0 diff --git a/tests/group/test_group_debug_logging.py b/tests/group/test_group_debug_logging.py index 0155cf63..ba54d187 100644 --- a/tests/group/test_group_debug_logging.py +++ b/tests/group/test_group_debug_logging.py @@ -35,7 +35,7 @@ def test_alter_group_request_debug_log_works_on_success( stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.group_admin.alter( - "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS + "testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS ) success_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(success_log, TestGroupConstants.TEST_ALTER_GROUP_SUCCESS_LOG) @@ -52,7 +52,7 @@ def test_alter_group_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.group_admin.alter( - "TESTGRP0", + "testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, ) except SecurityRequestError: @@ -72,7 +72,7 @@ def test_extract_group_base_omvs_request_debug_log_works_on_success( ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - self.group_admin.extract("TESTGRP0", segments=["omvs"]) + self.group_admin.extract("testgrp0", segments=["omvs"]) success_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( success_log, TestGroupConstants.TEST_EXTRACT_GROUP_BASE_OMVS_SUCCESS_LOG @@ -88,7 +88,7 @@ def test_extract_group_base_omvs_request_debug_log_works_on_error( stdout = io.StringIO() with contextlib.redirect_stdout(stdout): try: - self.group_admin.extract("TESTGRP0", segments=["omvs"]) + self.group_admin.extract("testgrp0", segments=["omvs"]) except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) diff --git a/tests/group/test_group_getters.py b/tests/group/test_group_getters.py index e532c699..efb0861e 100644 --- a/tests/group/test_group_getters.py +++ b/tests/group/test_group_getters.py @@ -28,7 +28,7 @@ def test_group_admin_has_group_special_authority_returns_true_when_group_special TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertTrue( - self.group_admin.has_group_special_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_special_authority("testgrp0", "ESWIFT") ) def test_group_admin_has_group_special_authority_returns_false_when_not_group_special( @@ -39,7 +39,7 @@ def test_group_admin_has_group_special_authority_returns_false_when_not_group_sp TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML ) self.assertFalse( - self.group_admin.has_group_special_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_special_authority("testgrp0", "ESWIFT") ) # Error in environment, TESTGRP0 already deleted/not added @@ -51,7 +51,7 @@ def test_group_admin_has_group_special_authority_raises_an_exception_when_extrac TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.has_group_special_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_special_authority("testgrp0", "ESWIFT") # ============================================================================ # Group Operations Authority @@ -64,7 +64,7 @@ def test_group_admin_has_group_operations_authority_returns_true_when_group_oper TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertTrue( - self.group_admin.has_group_operations_authority("TESTGRP0", "LEONARD") + self.group_admin.has_group_operations_authority("testgrp0", "LEONARD") ) def test_group_admin_has_group_operations_authority_returns_false_when_not_group_operations( @@ -75,7 +75,7 @@ def test_group_admin_has_group_operations_authority_returns_false_when_not_group TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML ) self.assertFalse( - self.group_admin.has_group_operations_authority("TESTGRP0", "LEONARD") + self.group_admin.has_group_operations_authority("testgrp0", "LEONARD") ) # Error in environment, TESTGRP0 already deleted/not added @@ -87,7 +87,7 @@ def test_group_admin_has_group_operations_authority_raises_an_exception_when_ext TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.has_group_operations_authority("TESTGRP0", "LEONARD") + self.group_admin.has_group_operations_authority("testgrp0", "LEONARD") # ============================================================================ # Group Auditor Authority @@ -105,7 +105,7 @@ def test_group_admin_has_group_auditor_authority_returns_true_when_group_auditor ) call_racf_mock.return_value = group_extract_auditor self.assertTrue( - self.group_admin.has_group_auditor_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_auditor_authority("testgrp0", "ESWIFT") ) def test_group_admin_has_group_auditor_authority_returns_false_when_not_group_auditor( @@ -116,7 +116,7 @@ def test_group_admin_has_group_auditor_authority_returns_false_when_not_group_au TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertFalse( - self.group_admin.has_group_auditor_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_auditor_authority("testgrp0", "ESWIFT") ) # Error in environment, TESTGRP0 already deleted/not added @@ -128,7 +128,7 @@ def test_group_admin_has_group_auditor_authority_raises_an_exception_when_extrac TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.has_group_auditor_authority("TESTGRP0", "ESWIFT") + self.group_admin.has_group_auditor_authority("testgrp0", "ESWIFT") # ============================================================================ # Group Access Attribute @@ -146,7 +146,7 @@ def test_group_admin_has_group_access_attribute_returns_true_when_grpacc( ) call_racf_mock.return_value = group_extract_grpacc self.assertTrue( - self.group_admin.has_group_access_attribute("TESTGRP0", "LEONARD") + self.group_admin.has_group_access_attribute("testgrp0", "LEONARD") ) def test_group_admin_has_group_access_attribute_returns_false_when_not_grpacc( @@ -157,7 +157,7 @@ def test_group_admin_has_group_access_attribute_returns_false_when_not_grpacc( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertFalse( - self.group_admin.has_group_access_attribute("TESTGRP0", "LEONARD") + self.group_admin.has_group_access_attribute("testgrp0", "LEONARD") ) # Error in environment, TESTGRP0 already deleted/not added @@ -169,7 +169,7 @@ def test_group_admin_has_group_access_attribute_raises_an_exception_when_extract TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.has_group_access_attribute("TESTGRP0", "LEONARD") + self.group_admin.has_group_access_attribute("testgrp0", "LEONARD") # ============================================================================ # OMVS GID @@ -181,7 +181,7 @@ def test_group_admin_get_omvs_gid_works( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML ) - self.assertEqual(self.group_admin.get_omvs_gid("TESTGRP0"), 1234567) + self.assertEqual(self.group_admin.get_omvs_gid("testgrp0"), 1234567) # Error in environment, SQUIDWRD already deleted/not added def test_group_admin_get_omvs_gid_raises_an_exception_when_extract_fails( @@ -192,7 +192,7 @@ def test_group_admin_get_omvs_gid_raises_an_exception_when_extract_fails( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.get_omvs_gid("TESTGRP0"), 1234567 + self.group_admin.get_omvs_gid("testgrp0"), 1234567 def test_group_admin_get_omvs_gid_returns_none_when_no_omvs_segment_exists( self, @@ -201,7 +201,7 @@ def test_group_admin_get_omvs_gid_returns_none_when_no_omvs_segment_exists( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) - self.assertIsNone(self.group_admin.get_omvs_gid("TESTGRP0")) + self.assertIsNone(self.group_admin.get_omvs_gid("testgrp0")) # ============================================================================ # OVM GID @@ -217,7 +217,7 @@ def test_group_admin_get_ovm_gid_works( "OMVS INFORMATION", "OVM INFORMATION" ) call_racf_mock.return_value = group_extract_ovm_gid - self.assertEqual(self.group_admin.get_ovm_gid("TESTGRP0"), 1234567) + self.assertEqual(self.group_admin.get_ovm_gid("testgrp0"), 1234567) # Error in environment, SQUIDWRD already deleted/not added def test_group_admin_get_ovm_gid_raises_an_exception_when_extract_fails( @@ -228,7 +228,7 @@ def test_group_admin_get_ovm_gid_raises_an_exception_when_extract_fails( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError): - self.group_admin.get_ovm_gid("TESTGRP0"), 1234567 + self.group_admin.get_ovm_gid("testgrp0"), 1234567 def test_group_admin_get_ovm_gid_returns_none_when_no_ovm_segment_exists( self, @@ -237,4 +237,4 @@ def test_group_admin_get_ovm_gid_returns_none_when_no_ovm_segment_exists( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) - self.assertIsNone(self.group_admin.get_ovm_gid("TESTGRP0")) + self.assertIsNone(self.group_admin.get_ovm_gid("testgrp0")) diff --git a/tests/group/test_group_request_builder.py b/tests/group/test_group_request_builder.py index ee9ee4c7..69c997c1 100644 --- a/tests/group/test_group_request_builder.py +++ b/tests/group/test_group_request_builder.py @@ -17,22 +17,22 @@ class TestGroupRequestBuilder(unittest.TestCase): def test_group_admin_build_add_group_request(self): result = self.group_admin.add( - "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + "testgrp0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ) self.assertEqual(result, TestGroupConstants.TEST_ADD_GROUP_REQUEST_XML) def test_group_admin_build_alter_group_request(self): result = self.group_admin.alter( - "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS + "testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS ) self.assertEqual(result, TestGroupConstants.TEST_ALTER_GROUP_REQUEST_XML) def test_group_admin_build_extract_group_request_base_omvs(self): - result = self.group_admin.extract("TESTGRP0", segments=["omvs"]) + result = self.group_admin.extract("testgrp0", segments=["omvs"]) self.assertEqual( result, TestGroupConstants.TEST_EXTRACT_GROUP_REQUEST_BASE_OMVS_XML ) def test_group_admin_build_delete_group_request(self): - result = self.group_admin.delete("TESTGRP0") + result = self.group_admin.delete("testgrp0") self.assertEqual(result, TestGroupConstants.TEST_DELETE_GROUP_REQUEST_XML) diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index 139e549a..d0105201 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -37,7 +37,7 @@ def test_group_admin_can_parse_add_group_success_xml( ] self.assertEqual( self.group_admin.add( - "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + "testgrp0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ), TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_DICTIONARY, ) @@ -46,7 +46,7 @@ def test_group_admin_throws_error_on_add_existing_group( self, call_racf_mock: Mock, ): - group_name = "TESTGRP0" + group_name = "testgrp0" call_racf_mock.side_effect = [ TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML, @@ -66,7 +66,7 @@ def test_group_admin_add_surfaces_error_from_preliminary_extract( self, call_racf_mock: Mock, ): - group_name = "TESTGRP0" + group_name = "testgrp0" extract_group_error_xml = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_XML ) @@ -124,7 +124,7 @@ def test_group_admin_can_parse_alter_group_success_xml( ] self.assertEqual( self.group_admin.alter( - "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS + "testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS ), TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_DICTIONARY, ) @@ -133,7 +133,7 @@ def test_group_admin_throws_error_on_alter_new_group( self, call_racf_mock: Mock, ): - group_name = "TESTGRP0" + group_name = "testgrp0" call_racf_mock.side_effect = [ TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML, TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_XML, @@ -160,7 +160,7 @@ def test_group_admin_can_parse_alter_group_error_xml( ] with self.assertRaises(SecurityRequestError) as exception: self.group_admin.alter( - "TESTGRP0", + "testgrp0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, ) self.assertEqual( @@ -179,7 +179,7 @@ def test_group_admin_can_parse_extract_group_base_omvs_success_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_XML ) self.assertEqual( - self.group_admin.extract("TESTGRP0", segments=["omvs"]), + self.group_admin.extract("testgrp0", segments=["omvs"]), TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_SUCCESS_DICTIONARY, ) @@ -191,7 +191,7 @@ def test_group_admin_can_parse_extract_group_base_only_success_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertEqual( - self.group_admin.extract("TESTGRP0"), + self.group_admin.extract("testgrp0"), TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_JSON, ) @@ -204,7 +204,7 @@ def test_group_admin_can_parse_extract_group_base_omvs_error_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) with self.assertRaises(SecurityRequestError) as exception: - self.group_admin.extract("TESTGRP0", segments=["omvs"]) + self.group_admin.extract("testgrp0", segments=["omvs"]) self.assertEqual( exception.exception.result, TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_DICTIONARY, @@ -221,7 +221,7 @@ def test_group_admin_can_parse_delete_group_success_xml( TestGroupConstants.TEST_DELETE_GROUP_RESULT_SUCCESS_XML ) self.assertEqual( - self.group_admin.delete("TESTGRP0"), + self.group_admin.delete("testgrp0"), TestGroupConstants.TEST_DELETE_GROUP_RESULT_SUCCESS_DICTIONARY, ) @@ -234,7 +234,7 @@ def test_group_admin_can_parse_delete_group_error_xml( TestGroupConstants.TEST_DELETE_GROUP_RESULT_ERROR_XML ) with self.assertRaises(SecurityRequestError) as exception: - self.group_admin.delete("TESTGRP0") + self.group_admin.delete("testgrp0") self.assertEqual( exception.exception.result, TestGroupConstants.TEST_DELETE_GROUP_RESULT_ERROR_DICTIONARY, diff --git a/tests/group/test_group_setters.py b/tests/group/test_group_setters.py index 209e64e6..db793c39 100644 --- a/tests/group/test_group_setters.py +++ b/tests/group/test_group_setters.py @@ -16,9 +16,9 @@ class TestGroupSetters(unittest.TestCase): group_admin = GroupAdmin(generate_requests_only=True) def test_group_admin_build_set_ovm_gid_request(self): - result = self.group_admin.set_ovm_gid("TESTGRP0", "1234567") + result = self.group_admin.set_ovm_gid("testgrp0", "1234567") self.assertEqual(result, TestGroupConstants.TEST_GROUP_SET_OVM_GID_XML) def test_group_admin_build_set_omvs_gid_request(self): - result = self.group_admin.set_omvs_gid("TESTGRP0", "1234567") + result = self.group_admin.set_omvs_gid("testgrp0", "1234567") self.assertEqual(result, TestGroupConstants.TEST_GROUP_SET_OMVS_GID_XML) diff --git a/tests/test_runner.py b/tests/test_runner.py index 4dcf5e3b..136675f0 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -11,7 +11,6 @@ from tests.access.test_access_request_builder import TestAccessRequestBuilder from tests.access.test_access_result_parser import TestAccessResultParser from tests.common.test_additional_secrets_redaction import ( - TestAdditionalSecretsLoggingRedaction, TestAdditionalSecretsResultRedaction, ) from tests.common.test_class_attributes import TestClassAttributes @@ -74,7 +73,6 @@ def __test_suite() -> unittest.TestSuite: TestAccessRequestBuilder, TestAccessDebugLogging, TestAdditionalSecretsResultRedaction, - TestAdditionalSecretsLoggingRedaction, TestClassAttributes, TestCustomizeSegmentTraits, TestSetupPrecheck,