From 12cd2b0bc25094c60ad47d36813ad139df94fda4 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 24 Nov 2023 14:06:18 -0500 Subject: [PATCH 01/30] Error and Error Handling Changes -Standardize SecurityRequestError Structure, merging IRRSMO00 Errors and that of RACF Errors with the smae internal structure in the result dictionary -Add an install script that defines IRR.IRRSMO00.PRECHECK with UACC of none and/or checks if the profile exists and the current user's access -Added NullResponseError -Added Unit Testing for Install script and null response error Signed-off-by: Elijah Swift --- pyracf/__init__.py | 2 + pyracf/common/null_response_error.py | 20 +++ pyracf/common/security_admin.py | 13 +- pyracf/common/security_request_error.py | 42 ++++- pyracf/scripts/install_precheck.py | 34 ++++ tests/common/test_common_constants.py | 51 ++++++ tests/common/test_install_precheck_script.py | 157 ++++++++++++++++++ tests/common/test_null_response_error.py | 34 ++++ .../add_group_result_error.json | 19 ++- ...ract_group_result_bad_attribute_error.json | 19 ++- tests/group/test_group_result_parser.py | 1 + ...rce_result_precheck_uacc_none_success.json | 32 ++++ ...urce_result_precheck_uacc_none_success.xml | 21 +++ ...extract_resource_result_precheck_error.xml | 14 ++ ...tract_resource_result_precheck_success.xml | 40 +++++ tests/test_runner.py | 4 + .../add_user_result_error.json | 19 ++- ...tract_user_result_bad_attribute_error.json | 19 ++- 18 files changed, 502 insertions(+), 39 deletions(-) create mode 100644 pyracf/common/null_response_error.py create mode 100644 pyracf/scripts/install_precheck.py create mode 100644 tests/common/test_common_constants.py create mode 100644 tests/common/test_install_precheck_script.py create mode 100644 tests/common/test_null_response_error.py create mode 100644 tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json create mode 100644 tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml create mode 100644 tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml create mode 100644 tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml diff --git a/pyracf/__init__.py b/pyracf/__init__.py index 9254e04e..d1f689b6 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,6 +2,7 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError +from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError from .common.segment_error import SegmentError from .common.segment_trait_error import SegmentTraitError @@ -9,5 +10,6 @@ from .data_set.data_set_admin import DataSetAdmin from .group.group_admin import GroupAdmin from .resource.resource_admin import ResourceAdmin +from .scripts.install_precheck import define_precheck_profile from .setropts.setropts_admin import SetroptsAdmin from .user.user_admin import UserAdmin diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py new file mode 100644 index 00000000..d3257e35 --- /dev/null +++ b/pyracf/common/null_response_error.py @@ -0,0 +1,20 @@ +"""Exception to use when no data is returned by IRRSMO00.""" + + +class NullResponseError(Exception): + """ + Raised when the no xml string is returned by IRRSMO00. + """ + + def __init__(self, xml_str: str) -> None: + self.message = "Security request made to IRRSMO00 failed." + self.xml_data = xml_str + self.message += ( + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + ) + self.message = f"({self.__class__.__name__}) {self.message}" + + def __str__(self) -> str: + return self.message diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index b1793e25..ec9e68b2 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -7,6 +7,7 @@ from .irrsmo00 import IRRSMO00 from .logger import Logger +from .null_response_error import NullResponseError from .security_request import SecurityRequest from .security_request_error import SecurityRequestError from .security_result import SecurityResult @@ -157,11 +158,11 @@ def _make_request( security_request.dump_request_xml(encoding="utf-8"), secret_traits=self.__secret_traits, ) + request_xml = self.__logger.redact_request_xml( + security_request.dump_request_xml(encoding="utf-8"), + secret_traits=self.__secret_traits, + ) if self._generate_requests_only: - request_xml = self.__logger.redact_request_xml( - security_request.dump_request_xml(encoding="utf-8"), - secret_traits=self.__secret_traits, - ) self.__clear_state(security_request) return request_xml result_xml = self.__logger.redact_result_xml( @@ -175,6 +176,8 @@ def _make_request( # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. self.__logger.log_xml("Result XML", result_xml) + if result_xml == "": + raise NullResponseError(result_xml) results = SecurityResult(result_xml) if self.__debug: # No need to redact anything here since the result dictionary @@ -189,7 +192,7 @@ def _make_request( # up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. - raise SecurityRequestError(result_dictionary) + raise SecurityRequestError(result_dictionary, request_xml) return result_dictionary def _to_steps(self, results: Union[List[dict], dict, bytes]) -> Union[dict, bytes]: diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 6a20bbeb..a44f0580 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -6,9 +6,10 @@ class SecurityRequestError(Exception): Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. """ - def __init__(self, result: dict) -> None: + def __init__(self, result: dict, request_xml: str) -> None: self.message = "Security request made to IRRSMO00 failed." - self.result = result + self.request_xml = request_xml + self.result = self.__check_result_type(result) self.message += ( "\n\nSee results dictionary " + f"'{self.__class__.__name__}.result' for more details." @@ -18,9 +19,46 @@ def __init__(self, result: dict) -> None: def __str__(self) -> str: return self.message + def __check_result_type(self, result: dict) -> dict: + not_profiles = ["returnCode", "reasonCode"] + for profile_type in result["securityResult"]: + if profile_type in not_profiles: + continue + if "error" in result["securityResult"][profile_type]: + return self.__organize_smo_error(result, profile_type) + return result + + def __organize_smo_error(self, result: dict, profile_type: str) -> dict: + command_result = {} + command_result["image"] = self.request_xml.decode("utf-8") + command_result["safReturnCode"] = result["securityResult"][profile_type][ + "error" + ]["errorFunction"] + command_result["returnCode"] = result["securityResult"][profile_type]["error"][ + "errorCode" + ] + command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ + "errorReason" + ] + error_offset = result["securityResult"][profile_type]["error"]["errorOffset"] + error_text = result["securityResult"][profile_type]["error"]["textInError"] + error_message = ( + result["securityResult"][profile_type]["error"]["errorMessage"] + + "\n" + + f"Text in Error: '{error_text}'\n" + + f"Approximate Offset of Text in Error: '{error_offset}'\n" + + "Please note that for any text in error," + + " redacted values may skew offset calculations." + ) + command_result["messages"] = [error_message] + result["securityResult"][profile_type]["commands"] = [command_result] + del result["securityResult"][profile_type]["error"] + return result + def contains_error_message( self, security_definition_tag: str, error_message_id: str ): + """Checks to see if specific error message id appears in the security request error""" commands = self.result["securityResult"][security_definition_tag].get( "commands" ) diff --git a/pyracf/scripts/install_precheck.py b/pyracf/scripts/install_precheck.py new file mode 100644 index 00000000..c1fb2154 --- /dev/null +++ b/pyracf/scripts/install_precheck.py @@ -0,0 +1,34 @@ +from pyracf import ResourceAdmin, SecurityRequestError + + +def define_precheck_profile(): + resource_admin = ResourceAdmin() + try: + access = resource_admin.get_my_access("IRR.IRRSMO00.PRECHECK", "XFACILIT") + except SecurityRequestError: + traits_precheck = {"base:universal_access": "None"} + result = resource_admin.add( + "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck + ) + print( + "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None." + + "\nContact your security administrator for READ access before using pyRACF." + + "\nOther users of pyRACF will also need to have at least read access." + + "\nYou may also need to REFRESH the `XFACILIT` class." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + ) + return result + if access: + print( + f"IRR.IRRSMO00.PRECHECK is already defined, and you already have {access} access!" + + "\nYou are ready to start using pyRACF!" + + "\nPlease ensure other users of pyRACF also have at least read access." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + ) + return True + print( + "IRR.IRRSMO00.PRECHECK is already defined, but you have no access." + + "\nContact your security administrator for READ access before using pyRACF." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + ) + return False diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py new file mode 100644 index 00000000..0642a5ad --- /dev/null +++ b/tests/common/test_common_constants.py @@ -0,0 +1,51 @@ +""" +Sample data for testing General Resource Profile Administration functions. +""" + +from typing import Union + +import tests.test_utilities as TestUtilities + + +def get_sample(sample_file: str) -> Union[str, bytes]: + return TestUtilities.get_sample(sample_file, "resource") + + +# ============================================================================ +# Install Precheck Sample Data +# ============================================================================ + +TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML = get_sample( + "extract_resource_result_precheck_error.xml" +) +TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML = get_sample( + "extract_resource_result_precheck_success.xml" +) + +TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_XML = get_sample( + "add_resource_result_precheck_uacc_none_success.xml" +) +TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY = get_sample( + "add_resource_result_precheck_uacc_none_success.json" +) + +TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT = ( + "IRR.IRRSMO00.PRECHECK is already defined, and you already have alter access!" + + "\nYou are ready to start using pyRACF!" + + "\nPlease ensure other users of pyRACF also have at least read access." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" +) + +TEST_INSTALL_PRECHECK_FOUND_NO_ACCESS_TEXT = ( + "IRR.IRRSMO00.PRECHECK is already defined, but you have no access." + + "\nContact your security administrator for READ access before using pyRACF." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" +) + +TEST_INSTALL_PRECHECK_DEFINED_PROFILE_TEXT = ( + "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None." + + "\nContact your security administrator for READ access before using pyRACF." + + "\nOther users of pyRACF will also need to have at least read access." + + "\nYou may also need to REFRESH the `XFACILIT` class." + + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" +) diff --git a/tests/common/test_install_precheck_script.py b/tests/common/test_install_precheck_script.py new file mode 100644 index 00000000..f599eb3c --- /dev/null +++ b/tests/common/test_install_precheck_script.py @@ -0,0 +1,157 @@ +"""Test password sanitization in resource debug logging.""" + +import contextlib +import io +import re +import unittest +from unittest.mock import Mock, patch + +import __init__ + +import tests.common.test_common_constants as TestCommonConstants +from pyracf import define_precheck_profile +from pyracf.common.irrsmo00 import IRRSMO00 + +# Resolves F401 +__init__ + + +@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") +class TestInstallPrecheckScript(unittest.TestCase): + maxDiff = None + IRRSMO00.__init__ = Mock(return_value=None) + ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + def test_install_precheck_works_when_no_setup_done( + self, + call_racf_mock: Mock, + ): + call_racf_mock.side_effect = [ + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML, + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML, + TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_DEFINED_PROFILE_TEXT + ) + self.assertEqual( + result, + TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY, + ) + + def test_install_precheck_works_when_alter_access_exists( + self, + call_racf_mock: Mock, + ): + call_racf_mock.return_value = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + precheck_log, + TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT, + ) + self.assertEqual(result, True) + + def test_install_precheck_works_when_control_access_exists( + self, + call_racf_mock: Mock, + ): + extract_with_control_access = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + extract_with_control_access = extract_with_control_access.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ CONTROL NO", + ) + call_racf_mock.return_value = extract_with_control_access + validated_control_access = ( + TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + ) + validated_control_access = validated_control_access.replace( + "you already have alter access!", "you already have control access!" + ) + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual(precheck_log, validated_control_access) + self.assertEqual(result, True) + + def test_install_precheck_works_when_read_access_exists( + self, + call_racf_mock: Mock, + ): + extract_with_read_access = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + extract_with_read_access = extract_with_read_access.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ READ NO", + ) + call_racf_mock.return_value = extract_with_read_access + validated_read_access = ( + TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + ) + validated_read_access = validated_read_access.replace( + "you already have alter access!", "you already have read access!" + ) + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual(precheck_log, validated_read_access) + self.assertEqual(result, True) + + def test_install_precheck_works_when_update_access_exists( + self, + call_racf_mock: Mock, + ): + extract_with_update_access = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + extract_with_update_access = extract_with_update_access.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ UPDATE NO", + ) + call_racf_mock.return_value = extract_with_update_access + validated_update_access = ( + TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + ) + validated_update_access = validated_update_access.replace( + "you already have alter access!", "you already have update access!" + ) + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual(precheck_log, validated_update_access) + self.assertEqual(result, True) + + def test_install_precheck_works_when_none_access_exists( + self, + call_racf_mock: Mock, + ): + extract_with_none_access = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + extract_with_none_access = extract_with_none_access.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ NONE NO", + ) + call_racf_mock.return_value = extract_with_none_access + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + result = define_precheck_profile() + precheck_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_FOUND_NO_ACCESS_TEXT + ) + self.assertEqual(result, False) diff --git a/tests/common/test_null_response_error.py b/tests/common/test_null_response_error.py new file mode 100644 index 00000000..f38cb354 --- /dev/null +++ b/tests/common/test_null_response_error.py @@ -0,0 +1,34 @@ +"""Test general resource profile result parser.""" + +import unittest +from unittest.mock import Mock, patch + +import __init__ + +from pyracf import NullResponseError, UserAdmin +from pyracf.common.irrsmo00 import IRRSMO00 + +# Resolves F401 +__init__ + + +@patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") +class TestNullResponseError(unittest.TestCase): + maxDiff = None + IRRSMO00.__init__ = Mock(return_value=None) + user_admin = UserAdmin() + + def test_null_response_error_thrown_on_null_response( + self, + call_racf_mock: Mock, + ): + call_racf_mock.return_value = "" + with self.assertRaises(NullResponseError) as exception: + self.user_admin.set_password("TESTUSER", "Testpass") + self.assertEqual( + exception.exception.message, + "(NullResponseError) Security request made to IRRSMO00 failed." + + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class.", + ) diff --git a/tests/group/group_result_samples/add_group_result_error.json b/tests/group/group_result_samples/add_group_result_error.json index 4df6b394..eb939933 100644 --- a/tests/group/group_result_samples/add_group_result_error.json +++ b/tests/group/group_result_samples/add_group_result_error.json @@ -4,14 +4,17 @@ "name": "TESTGRPP0", "operation": "set", "requestId": "GroupRequest", - "error": { - "errorFunction": 10, - "errorCode": 2000, - "errorReason": 68, - "errorMessage": "Invalid attribute value specified.", - "errorOffset": 149, - "textInError": "name" - } + "commands": [ + { + "safReturnCode": 10, + "returnCode": 2000, + "reasonCode": 68, + "image": "", + "messages": [ + "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + ] + } + ] }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json index 1ef0740a..429bb8a2 100644 --- a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json +++ b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json @@ -4,14 +4,17 @@ "name": "TESTGRPP0", "operation": "listdata", "requestId": "GroupRequest", - "error": { - "errorFunction": 10, - "errorCode": 2000, - "errorReason": 68, - "errorMessage": "Invalid attribute value specified.", - "errorOffset": 149, - "textInError": "name" - } + "commands": [ + { + "safReturnCode": 10, + "returnCode": 2000, + "reasonCode": 68, + "image": "", + "messages": [ + "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + ] + } + ] }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index b02441da..bbe78ba8 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -75,6 +75,7 @@ def test_group_admin_can_parse_add_group_error_xml( self.group_admin.add( "TESTGRPP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ) + print(exception.exception.result) self.assertEqual( exception.exception.result, TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BAD_ATTRIBUTE_ERROR_DICTIONARY, diff --git a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json new file mode 100644 index 00000000..135b5417 --- /dev/null +++ b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json @@ -0,0 +1,32 @@ +{ + "securityResult": { + "resource": { + "name": "IRR.IRRSMO00.PRECHECK", + "class": "XFACILIT", + "operation": "set", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RDEFINE XFACILIT (IRR.IRRSMO00.PRECHECK) ", + "messages": [ + "ICH10006I RACLISTED PROFILES FOR XFACILIT WILL NOT REFLECT THE ADDITION(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + }, + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RALTER XFACILIT (IRR.IRRSMO00.PRECHECK) UACC (None) ", + "messages": [ + "ICH11009I RACLISTED PROFILES FOR XFACILIT WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} \ No newline at end of file diff --git a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml new file mode 100644 index 00000000..210546b8 --- /dev/null +++ b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml @@ -0,0 +1,21 @@ + + + + + 0 + 0 + 0 + RDEFINE XFACILIT (IRR.IRRSMO00.PRECHECK) + ICH10006I RACLISTED PROFILES FOR XFACILIT WILL NOT REFLECT THE ADDITION(S) UNTIL A SETROPTS REFRESH IS ISSUED. + + + 0 + 0 + 0 + RALTER XFACILIT (IRR.IRRSMO00.PRECHECK) UACC (None) + ICH11009I RACLISTED PROFILES FOR XFACILIT 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/extract_resource_result_precheck_error.xml b/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml new file mode 100644 index 00000000..21315a99 --- /dev/null +++ b/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml @@ -0,0 +1,14 @@ + + + + + 8 + 16 + 4 + RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) + ICH13003I TESTING NOT FOUND + + + 4 + 0 + \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml b/tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml new file mode 100644 index 00000000..14c45774 --- /dev/null +++ b/tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml @@ -0,0 +1,40 @@ + + + + + 0 + 0 + 0 + RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) + CLASS NAME + ----- ---- + XFACILIT IRR.IRRSMO00.PRECHECK + + GROUP CLASS NAME + ----- ----- ---- + GXFACILI + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ ALTER NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + \ No newline at end of file diff --git a/tests/test_runner.py b/tests/test_runner.py index 6253f828..7c55803d 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -8,7 +8,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_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger +from tests.common.test_null_response_error import TestNullResponseError from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -59,7 +61,9 @@ def __test_suite() -> unittest.TestSuite: TestAccessResultParser, TestAccessRequestBuilder, TestAccessDebugLogging, + TestInstallPrecheckScript, TestLogger, + TestNullResponseError, TestConnectionResultParser, TestConnectionRequestBuilder, TestConnectionSetters, diff --git a/tests/user/user_result_samples/add_user_result_error.json b/tests/user/user_result_samples/add_user_result_error.json index 543c008a..83223ee8 100644 --- a/tests/user/user_result_samples/add_user_result_error.json +++ b/tests/user/user_result_samples/add_user_result_error.json @@ -4,14 +4,17 @@ "name": "squidward", "operation": "set", "requestId": "UserRequest", - "error": { - "errorFunction": 10, - "errorCode": 2000, - "errorReason": 68, - "errorMessage": "Invalid attribute value specified.", - "errorOffset": 149, - "textInError": "name" - } + "commands": [ + { + "safReturnCode": 10, + "returnCode": 2000, + "reasonCode": 68, + "image": "", + "messages": [ + "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + ] + } + ] }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json index c15ae7cf..4a2f4e7a 100644 --- a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json +++ b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json @@ -4,14 +4,17 @@ "name": "squidward", "operation": "listdata", "requestId": "UserRequest", - "error": { - "errorFunction": 10, - "errorCode": 2000, - "errorReason": 68, - "errorMessage": "Invalid attribute value specified.", - "errorOffset": 149, - "textInError": "name" - } + "commands": [ + { + "safReturnCode": 10, + "returnCode": 2000, + "reasonCode": 68, + "image": "", + "messages": [ + "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + ] + } + ] }, "returnCode": 2000, "reasonCode": 68 From f5754c23eddde01754f78aa0462fe2d5815a86fa Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 24 Nov 2023 16:38:40 -0500 Subject: [PATCH 02/30] Add base functionality to run as another user -Add parameters to irrsmo00.c for userid and userid_len -Add code in irrsmo00 that can process a userid and pass it and length to c code -Add methods to security admin object that allow for changing executing userID -Add ImproperUSerIDError to enforce userid changes to theoretically valid ids Signed-off-by: Elijah Swift --- pyracf/common/improper_userid_error.py | 17 ++++++++++++++ pyracf/common/irrsmo00.c | 8 ++++--- pyracf/common/irrsmo00.py | 18 +++++++++++++-- pyracf/common/security_admin.py | 31 +++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 pyracf/common/improper_userid_error.py diff --git a/pyracf/common/improper_userid_error.py b/pyracf/common/improper_userid_error.py new file mode 100644 index 00000000..60e24f6e --- /dev/null +++ b/pyracf/common/improper_userid_error.py @@ -0,0 +1,17 @@ +"""Exception to use when attempting to set an invalid userid for pyRACF to run under.""" + + +class ImproperUserIDError(Exception): + """ + Raised when pyRACF would attempt to establish itself to + run under a passed 'userid' that is not a string of 1 to 8 characters. + """ + + def __init__(self, userid: str) -> None: + self.message = ( + "Cannot run under this userid." + + f"{userid} is not a string from 1 to 8 characters in length." + ) + + def __str__(self) -> str: + return self.message diff --git a/pyracf/common/irrsmo00.c b/pyracf/common/irrsmo00.c index 8ebdc8d4..f66b431c 100644 --- a/pyracf/common/irrsmo00.c +++ b/pyracf/common/irrsmo00.c @@ -33,18 +33,20 @@ void null_byte_fix(char* str, unsigned int str_len) { static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) { const unsigned int xml_len; const unsigned int input_opts; + const uint8_t input_userid_len; const char *input_xml; + const char *input_userid; - static char *kwlist[] = {"xml_str", "xml_len", "opts", NULL}; + static char *kwlist[] = {"xml_str", "xml_len", "opts", "userid", "userid_len", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y|II", kwlist, &input_xml, &xml_len, &input_opts)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y|IIyb", kwlist, &input_xml, &xml_len, &input_opts, &input_userid, &input_userid_len)) { return NULL; } char work_area[1024]; char req_handle[64] = { 0 }; - VarStr_T userid = { 0, {0}}; + VarStr_T userid = { input_userid_len, {input_userid}}; unsigned int alet = 0; unsigned int acee = 0; unsigned char rsp[BUFFER_SIZE+1]; diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index d8a503d6..f6dd8549 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -1,5 +1,6 @@ """Interface to irrsmo00.dll.""" import platform +from typing import Union try: from cpyracf import call_irrsmo00 @@ -19,9 +20,22 @@ def __init__(self) -> None: # Initialize size of output buffer self.buffer_size = 100000 - def call_racf(self, request_xml: bytes, precheck: bool = False) -> str: + def call_racf( + self, + request_xml: bytes, + precheck: bool = False, + running_userid: Union[str, False] = False, + ) -> str: """Make request to call_irrsmo00 in the cpyracf Python extension.""" options = 15 if precheck else 13 + if not running_userid: + return call_irrsmo00( + xml_str=request_xml, xml_len=len(request_xml), opts=options + ).decode("cp1047") return call_irrsmo00( - xml_str=request_xml, xml_len=len(request_xml), opts=options + xml_str=request_xml, + xml_len=len(request_xml), + opts=options, + userid=running_userid.encode("cp1047"), + userid_len=len(running_userid), ).decode("cp1047") diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index b1793e25..7e91c1e2 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -5,6 +5,7 @@ from datetime import datetime from typing import Any, List, Tuple, Union +from .improper_userid_error import ImproperUserIDError from .irrsmo00 import IRRSMO00 from .logger import Logger from .security_request import SecurityRequest @@ -20,6 +21,7 @@ class SecurityAdmin: _valid_segment_traits = {} _extracted_key_value_pair_segment_traits_map = {} _case_sensitive_extracted_values = [] + __running_userid = False __logger = Logger() def __init__( @@ -30,6 +32,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, False] = False, ) -> None: self._common_base_traits_data_set_generic = { "base:aclcnt": "racf:aclcnt", @@ -79,6 +82,30 @@ def __init__( self.__replace_valid_segment_traits(replace_existing_segment_traits) if additional_secret_traits is not None: self.__add_additional_secret_traits(additional_secret_traits) + if run_as_userid: + self.set_running_userid(run_as_userid) + + # ============================================================================ + # Run as Other User ID + # ============================================================================ + def set_running_userid(self, new_userid: Union[str, False]): + if new_userid is False: + self.__running_userid = new_userid + return + if ( + new_userid.isinstance(str) + and (len(new_userid) <= 8) + and (not new_userid == "") + ): + self.__running_userid = new_userid + return + raise ImproperUserIDError(new_userid) + + def clear_running_userid(self): + self.__running_userid = False + + def get_running_userid(self): + return self.__running_userid # ============================================================================ # Customize Segment Traits @@ -166,7 +193,9 @@ def _make_request( return request_xml result_xml = self.__logger.redact_result_xml( self.__irrsmo00.call_racf( - security_request.dump_request_xml(), irrsmo00_precheck + security_request.dump_request_xml(), + irrsmo00_precheck, + self.__running_userid, ), self.__secret_traits, ) From 721e878c66c207e79375d5dd923d61818494338c Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 24 Nov 2023 16:41:59 -0500 Subject: [PATCH 03/30] Typing changes Make changes to be compatible with 3.10 typing library Signed-off-by: Elijah Swift --- pyracf/common/irrsmo00.c | 5 ++++- pyracf/common/irrsmo00.py | 8 ++++++-- pyracf/common/security_admin.py | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pyracf/common/irrsmo00.c b/pyracf/common/irrsmo00.c index f66b431c..08b48a92 100644 --- a/pyracf/common/irrsmo00.c +++ b/pyracf/common/irrsmo00.c @@ -3,6 +3,7 @@ #include #include #include +#include #define BUFFER_SIZE (100000) @@ -46,7 +47,7 @@ static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) char work_area[1024]; char req_handle[64] = { 0 }; - VarStr_T userid = { input_userid_len, {input_userid}}; + VarStr_T userid = { input_userid_len, {0}}; unsigned int alet = 0; unsigned int acee = 0; unsigned char rsp[BUFFER_SIZE+1]; @@ -54,6 +55,8 @@ static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) unsigned int saf_rc=0, racf_rc=0, racf_rsn=0; unsigned int num_parms=17, fn=1, opts = input_opts, rsp_len = sizeof(rsp)-1; + strncpy(userid.str, input_userid, userid.len); + IRRSMO64( work_area, alet, diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index f6dd8549..8057db09 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -24,13 +24,17 @@ def call_racf( self, request_xml: bytes, precheck: bool = False, - running_userid: Union[str, False] = False, + running_userid: Union[str, bool] = False, ) -> str: """Make request to call_irrsmo00 in the cpyracf Python extension.""" options = 15 if precheck else 13 if not running_userid: return call_irrsmo00( - xml_str=request_xml, xml_len=len(request_xml), opts=options + xml_str=request_xml, + xml_len=len(request_xml), + opts=options, + userid=0, + userid_len=0, ).decode("cp1047") return call_irrsmo00( xml_str=request_xml, diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 7e91c1e2..a44f0d77 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -32,7 +32,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, - run_as_userid: Union[str, False] = False, + run_as_userid: Union[str, bool] = False, ) -> None: self._common_base_traits_data_set_generic = { "base:aclcnt": "racf:aclcnt", @@ -88,7 +88,7 @@ def __init__( # ============================================================================ # Run as Other User ID # ============================================================================ - def set_running_userid(self, new_userid: Union[str, False]): + def set_running_userid(self, new_userid: Union[str, bool]): if new_userid is False: self.__running_userid = new_userid return @@ -97,7 +97,7 @@ def set_running_userid(self, new_userid: Union[str, False]): and (len(new_userid) <= 8) and (not new_userid == "") ): - self.__running_userid = new_userid + self.__running_userid = new_userid.upper() return raise ImproperUserIDError(new_userid) From a6e53c495a058fee860ee065d7bbd19eea7beedd Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 24 Nov 2023 17:23:07 -0500 Subject: [PATCH 04/30] Update irrsmo00.py Change type of default userid parm passed to irrsmo00. Signed-off-by: Elijah Swift --- pyracf/common/irrsmo00.py | 2 +- pyracf/common/security_admin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index 8057db09..816b981b 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -33,7 +33,7 @@ def call_racf( xml_str=request_xml, xml_len=len(request_xml), opts=options, - userid=0, + userid="".encode("cp1047"), userid_len=0, ).decode("cp1047") return call_irrsmo00( diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index a44f0d77..6a647dba 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -93,7 +93,7 @@ def set_running_userid(self, new_userid: Union[str, bool]): self.__running_userid = new_userid return if ( - new_userid.isinstance(str) + isinstance(new_userid, str) and (len(new_userid) <= 8) and (not new_userid == "") ): From f4756c74314554bc1dffb9bfb3675360c6eeba8d Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Sat, 25 Nov 2023 11:47:18 -0500 Subject: [PATCH 05/30] Surface IRRSMO00 Return and Reason Codes Also allow for run as userid to be specified in object creation Signed-off-by: Elijah Swift --- pyracf/access/access_admin.py | 2 ++ pyracf/common/irrsmo00.c | 4 ++-- pyracf/common/irrsmo00.py | 11 +++++++---- pyracf/common/security_admin.py | 10 +++++----- pyracf/connection/connection_admin.py | 2 ++ pyracf/data_set/data_set_admin.py | 2 ++ pyracf/group/group_admin.py | 2 ++ pyracf/resource/resource_admin.py | 2 ++ pyracf/setropts/setropts_admin.py | 2 ++ pyracf/user/user_admin.py | 2 ++ 10 files changed, 28 insertions(+), 11 deletions(-) diff --git a/pyracf/access/access_admin.py b/pyracf/access/access_admin.py index 46a13e4e..32796da8 100644 --- a/pyracf/access/access_admin.py +++ b/pyracf/access/access_admin.py @@ -17,6 +17,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -48,6 +49,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ diff --git a/pyracf/common/irrsmo00.c b/pyracf/common/irrsmo00.c index 08b48a92..56f7b3ef 100644 --- a/pyracf/common/irrsmo00.c +++ b/pyracf/common/irrsmo00.c @@ -78,11 +78,11 @@ static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) ); null_byte_fix(rsp,rsp_len); - return Py_BuildValue("y", rsp); + return Py_BuildValue("yBBB", rsp, saf_rc, racf_rc, racf_rsn); } static char call_irrsmo00_docs[] = - "call_irrsmo00(input_xml: bytes, xml_len: uint, opts: uint): Returns an XML response from the IRRSMO00 RACF Callable Service.\n"; + "call_irrsmo00(input_xml: bytes, xml_len: uint, opts: uint): Returns an XML response string and return and reason codes from the IRRSMO00 RACF Callable Service.\n"; static PyMethodDef cpyracf_methods[] = { {"call_irrsmo00", (PyCFunction)call_irrsmo00, diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index 816b981b..b789d03f 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -24,22 +24,25 @@ def call_racf( self, request_xml: bytes, precheck: bool = False, - running_userid: Union[str, bool] = False, + running_userid: Union[str, bool] = None, ) -> str: """Make request to call_irrsmo00 in the cpyracf Python extension.""" options = 15 if precheck else 13 - if not running_userid: - return call_irrsmo00( + if running_userid is None: + response = call_irrsmo00( xml_str=request_xml, xml_len=len(request_xml), opts=options, userid="".encode("cp1047"), userid_len=0, ).decode("cp1047") - return call_irrsmo00( + response = call_irrsmo00( xml_str=request_xml, xml_len=len(request_xml), opts=options, userid=running_userid.encode("cp1047"), userid_len=len(running_userid), ).decode("cp1047") + if response[0] == "": + return response[1:3] + return response[0] diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 6a647dba..293ddbf0 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -32,7 +32,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, - run_as_userid: Union[str, bool] = False, + run_as_userid: Union[str, None] = None, ) -> None: self._common_base_traits_data_set_generic = { "base:aclcnt": "racf:aclcnt", @@ -82,14 +82,14 @@ def __init__( self.__replace_valid_segment_traits(replace_existing_segment_traits) if additional_secret_traits is not None: self.__add_additional_secret_traits(additional_secret_traits) - if run_as_userid: + if run_as_userid is not None: self.set_running_userid(run_as_userid) # ============================================================================ # Run as Other User ID # ============================================================================ - def set_running_userid(self, new_userid: Union[str, bool]): - if new_userid is False: + def set_running_userid(self, new_userid: Union[str, None]): + if new_userid is None: self.__running_userid = new_userid return if ( @@ -102,7 +102,7 @@ def set_running_userid(self, new_userid: Union[str, bool]): raise ImproperUserIDError(new_userid) def clear_running_userid(self): - self.__running_userid = False + self.__running_userid = None def get_running_userid(self): return self.__running_userid diff --git a/pyracf/connection/connection_admin.py b/pyracf/connection/connection_admin.py index 2527118e..0162ccfb 100644 --- a/pyracf/connection/connection_admin.py +++ b/pyracf/connection/connection_admin.py @@ -17,6 +17,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -40,6 +41,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index 1995e5af..2b1df499 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -20,6 +20,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -67,6 +68,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) self._valid_segment_traits["base"].update( self._common_base_traits_data_set_generic diff --git a/pyracf/group/group_admin.py b/pyracf/group/group_admin.py index 63b22f7b..bd78851d 100644 --- a/pyracf/group/group_admin.py +++ b/pyracf/group/group_admin.py @@ -20,6 +20,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -51,6 +52,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index ac5720d6..c67ea542 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -20,6 +20,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -241,6 +242,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ diff --git a/pyracf/setropts/setropts_admin.py b/pyracf/setropts/setropts_admin.py index 1aa6505a..f8e7f041 100644 --- a/pyracf/setropts/setropts_admin.py +++ b/pyracf/setropts/setropts_admin.py @@ -17,6 +17,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -114,6 +115,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ diff --git a/pyracf/user/user_admin.py b/pyracf/user/user_admin.py index beba75cd..6d1deb9a 100644 --- a/pyracf/user/user_admin.py +++ b/pyracf/user/user_admin.py @@ -20,6 +20,7 @@ def __init__( update_existing_segment_traits: Union[dict, None] = None, replace_existing_segment_traits: Union[dict, None] = None, additional_secret_traits: Union[List[str], None] = None, + run_as_userid: Union[str, None] = None, ) -> None: self._valid_segment_traits = { "base": { @@ -212,6 +213,7 @@ def __init__( update_existing_segment_traits=update_existing_segment_traits, replace_existing_segment_traits=replace_existing_segment_traits, additional_secret_traits=additional_secret_traits, + run_as_userid=run_as_userid, ) # ============================================================================ From 85e7109ad4ce5eb420c09589906fe72dd17e24af Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Sat, 25 Nov 2023 21:06:08 -0500 Subject: [PATCH 06/30] Fold Surrogat Error into Null Response -Fold Surrogat error (run as userid attempted with no access defined) that yields no response into Null Response Error -Use surfaced return and reason codes from IRRSMO00 to differentiate null response errors -Add get_user_access to resource admin -Add unit testing for all of it -move custom traits testing under common folder Signed-off-by: Elijah Swift --- pyracf/__init__.py | 1 + pyracf/common/logger.py | 12 +- pyracf/common/null_response_error.py | 41 ++- pyracf/common/security_admin.py | 12 +- pyracf/common/security_request_error.py | 2 +- pyracf/common/security_result.py | 7 +- pyracf/resource/resource_admin.py | 15 +- tests/common/test_common_constants.py | 103 ++++++- tests/common/test_customize_segment_traits.py | 66 +++++ tests/common/test_null_response_error.py | 37 ++- tests/common/test_run_as_userid.py | 162 +++++++++++ .../extract_resource_precheck_as_squidwrd.log | 171 ++++++++++++ tests/test_runner.py | 4 + tests/user/test_user_constants.py | 40 --- tests/user/test_user_request_builder.py | 29 -- tests/user/test_user_result_parser.py | 18 -- .../alter_user_success_as_eswift.log | 263 ++++++++++++++++++ 17 files changed, 861 insertions(+), 122 deletions(-) create mode 100644 tests/common/test_customize_segment_traits.py create mode 100644 tests/common/test_run_as_userid.py create mode 100644 tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log create mode 100644 tests/user/user_log_samples/alter_user_success_as_eswift.log diff --git a/pyracf/__init__.py b/pyracf/__init__.py index d1f689b6..13da5008 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,6 +2,7 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError +from .common.improper_userid_error import ImproperUserIDError from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError from .common.segment_error import SegmentError diff --git a/pyracf/common/logger.py b/pyracf/common/logger.py index 805a6222..59bd2200 100644 --- a/pyracf/common/logger.py +++ b/pyracf/common/logger.py @@ -4,7 +4,7 @@ import json import os import re -from typing import Union +from typing import List, Union class Logger: @@ -161,7 +161,7 @@ def redact_request_xml( def redact_result_xml( self, - xml_string: str, + xml_response: Union[str, List[int]], secret_traits: dict, ) -> str: """ @@ -170,13 +170,15 @@ def redact_result_xml( 'TRAIT (value)' This function also accounts for varied amounts of whitespace in the pattern. """ + if isinstance(xml_response, list): + return xml_response for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - match = re.search(rf"{racf_key.upper()} +\(", xml_string) + match = re.search(rf"{racf_key.upper()} +\(", xml_response) if not match: continue - xml_string = self.__redact_string(xml_string, match.end(), ")") - return xml_string + xml_response = self.__redact_string(xml_response, match.end(), ")") + return xml_response def __colorize_json(self, json_text: str) -> str: updated_json_text = "" diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py index d3257e35..e8d087cc 100644 --- a/pyracf/common/null_response_error.py +++ b/pyracf/common/null_response_error.py @@ -1,19 +1,44 @@ """Exception to use when no data is returned by IRRSMO00.""" +from typing import Union class NullResponseError(Exception): """ - Raised when the no xml string is returned by IRRSMO00. + Raised when no xml string is returned by IRRSMO00. """ - def __init__(self, xml_str: str) -> None: + def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None: self.message = "Security request made to IRRSMO00 failed." - self.xml_data = xml_str - self.message += ( - "\n\nCheck to see if proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." - ) + self.saf_return_code = xml_str[0] + self.racf_return_code = xml_str[1] + self.racf_reason_code = xml_str[2] + if ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 16) + ): + self.message += ( + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + ) + elif ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 8) + ): + self.message += ( + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For the `run_as_userid` feature, you must have at least ALTER" + + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." + ) + else: + self.message += ( + "\n\nPlease check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "(https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes)" + ) self.message = f"({self.__class__.__name__}) {self.message}" def __str__(self) -> str: diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 800d6dba..a28e990d 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -83,8 +83,7 @@ def __init__( self.__replace_valid_segment_traits(replace_existing_segment_traits) if additional_secret_traits is not None: self.__add_additional_secret_traits(additional_secret_traits) - if run_as_userid is not None: - self.set_running_userid(run_as_userid) + self.set_running_userid(run_as_userid) # ============================================================================ # Run as Other User ID @@ -201,13 +200,16 @@ def _make_request( self.__secret_traits, ) self.__clear_state(security_request) + if isinstance(result_xml, list): + # When IRRSMO00 encounters some errors, it returns no XML response string. + # When this happens, the c code instead surfaces the return and reason + # codes which causes a NullResponseError to be raised. + raise NullResponseError(result_xml, self.get_running_userid()) if self.__debug: # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. self.__logger.log_xml("Result XML", result_xml) - if result_xml == "": - raise NullResponseError(result_xml) - results = SecurityResult(result_xml) + results = SecurityResult(result_xml, self.get_running_userid()) if self.__debug: # No need to redact anything here since the result dictionary # already has secrets redacted when it is built. diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index a44f0580..4584d0ca 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -20,7 +20,7 @@ def __str__(self) -> str: return self.message def __check_result_type(self, result: dict) -> dict: - not_profiles = ["returnCode", "reasonCode"] + not_profiles = ["returnCode", "reasonCode", "runningUserid"] for profile_type in result["securityResult"]: if profile_type in not_profiles: continue diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index dba11ae2..b474cf83 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -1,6 +1,7 @@ """Generic Security Result Parser.""" import re +from typing import Union from xml.etree.ElementTree import Element # Only used for type hints. import defusedxml.ElementTree as XMLParser @@ -9,10 +10,14 @@ class SecurityResult: """Generic Security Result Parser.""" - def __init__(self, result_xml: str) -> None: + def __init__( + self, result_xml: str, running_userid: Union[str, None] = None + ) -> None: self.__result = XMLParser.fromstring(result_xml) self.__result_dictionary = {"securityResult": {}} self.__extract_results() + if running_userid is not None: + self.__result_dictionary["securityResult"]["runningUserid"] = running_userid def __extract_results(self) -> None: """Extract XML results into a dictionary.""" diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index c67ea542..87d6cd64 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -267,9 +267,22 @@ def set_universal_access( ) return self._to_steps(result) + # ============================================================================ + # Individual Access + # ============================================================================ def get_my_access(self, resource: str, class_name: str) -> Union[str, bytes, None]: - """Get the access associated with your own general resource profile.""" + """Get your own level of access associated with a general resource profile.""" + profile = self.extract(resource, class_name, profile_only=True) + return self._get_field(profile, "base", "yourAccess") + + def get_user_access( + self, resource: str, class_name: str, userid: str + ) -> Union[str, bytes, None]: + """Get a target user's own level of access associated with a general resource profile.""" + original_userid = self.get_running_userid() + self.set_running_userid(userid) profile = self.extract(resource, class_name, profile_only=True) + self.set_running_userid(original_userid) return self._get_field(profile, "base", "yourAccess") # ============================================================================ diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 0642a5ad..8324a797 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -1,5 +1,5 @@ """ -Sample data for testing General Resource Profile Administration functions. +Sample data for testing Common functions in pyracf. """ from typing import Union @@ -7,8 +7,8 @@ import tests.test_utilities as TestUtilities -def get_sample(sample_file: str) -> Union[str, bytes]: - return TestUtilities.get_sample(sample_file, "resource") +def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: + return TestUtilities.get_sample(sample_file, function_group) # ============================================================================ @@ -16,17 +16,17 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # ============================================================================ TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML = get_sample( - "extract_resource_result_precheck_error.xml" + "extract_resource_result_precheck_error.xml", "resource" ) TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML = get_sample( - "extract_resource_result_precheck_success.xml" + "extract_resource_result_precheck_success.xml", "resource" ) TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_XML = get_sample( - "add_resource_result_precheck_uacc_none_success.xml" + "add_resource_result_precheck_uacc_none_success.xml", "resource" ) TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY = get_sample( - "add_resource_result_precheck_uacc_none_success.json" + "add_resource_result_precheck_uacc_none_success.json", "resource" ) TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT = ( @@ -49,3 +49,92 @@ def get_sample(sample_file: str) -> Union[str, bytes]: + "\nYou may also need to REFRESH the `XFACILIT` class." + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) + +# ============================================================================ +# Customize Segment Traits +# ============================================================================ + +# Alter User Traits +TEST_ALTER_USER_CSDATA_AND_OMVS_REQUEST_TRAITS = { + "base:special": False, + "omvs:home_directory": "/u/clarinet", + "omvs:default_shell": False, + "csdata:tstcsfld": "testval", +} +TEST_ALTER_USER_CSDATA_REQUEST_TRAITS = { + "base:special": False, + "csdata:tstcsfld": "testval", +} + +# Valid Segment Traits Updates +TEST_USER_REPLACE_SEGMENT_TRAITS = { + "base": {"base:special": "alt:special"}, + "csdata": {"csdata:tstcsfld": "tstcsfld"}, +} + +TEST_USER_ADDITIONAL_SEGMENT_TRAITS = {"csdata": {"csdata:tstcsfld": "tstcsfld"}} + +# Alter User Requests +TEST_ALTER_USER_REQUEST_REPLACE_SEGMENTS_XML = get_sample( + "alter_user_request_replace_segments.xml", "user" +) +TEST_ALTER_USER_REQUEST_UPDATE_SEGMENTS_XML = get_sample( + "alter_user_request_update_segments.xml", "user" +) + +# Extract User Results +TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_XML = get_sample( + "extract_user_result_base_omvs_csdata_success.xml", "user" +) +TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_DICTIONARY = get_sample( + "extract_user_result_base_omvs_csdata_success.json", "user" +) + +# ============================================================================ +# Run As UserID +# ============================================================================ + +TEST_RUNNING_USERID = "ESWIFT" +TEST_ALTER_USER_REQUEST_TRAITS = { + "base:special": False, + "omvs:home_directory": "/u/clarinet", + "omvs:default_shell": False, +} +TEST_ALTER_USER_REQUEST_XML = get_sample("alter_user_request.xml", "user") +TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML = get_sample( + "extract_user_result_base_only_success.xml", "user" +) +TEST_ALTER_USER_SUCCESS_LOG = get_sample("alter_user_success.log", "user") +TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG = get_sample( + "alter_user_success_as_eswift.log", "user" +) +TEST_ALTER_USER_RESULT_SUCCESS_XML = get_sample("alter_user_result_success.xml", "user") +TEST_EXTRACT_RESOURCE_PRECHECK_AS_SQUIDWRD_LOG = get_sample( + "extract_resource_precheck_as_squidwrd.log", "resource" +) + +# ============================================================================ +# Null Response Error +# ============================================================================ + +TEST_NULL_RESPONSE_PRECHECK_TEXT = ( + "(NullResponseError) Security request made to IRRSMO00 failed." + + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." +) + +TEST_NULL_RESPONSE_SURROGAT_TEXT = ( + "(NullResponseError) Security request made to IRRSMO00 failed." + + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "For the `run_as_userid` feature, you must have at least ALTER" + + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." +) + +TEST_NULL_RESPONSE_GENERIC_TEXT = ( + "(NullResponseError) Security request made to IRRSMO00 failed." + + "\n\nPlease check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "(https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes)" +) diff --git a/tests/common/test_customize_segment_traits.py b/tests/common/test_customize_segment_traits.py new file mode 100644 index 00000000..80632c1c --- /dev/null +++ b/tests/common/test_customize_segment_traits.py @@ -0,0 +1,66 @@ +"""Test customizing security admin segment traits.""" + +import unittest +from unittest.mock import Mock, patch + +import __init__ + +import tests.common.test_common_constants as TestCommonConstants +from pyracf import UserAdmin +from pyracf.common.irrsmo00 import IRRSMO00 + +# Resolves F401 +__init__ + + +class TestCustomizeSegmentTraits(unittest.TestCase): + maxDiff = None + IRRSMO00.__init__ = Mock(return_value=None) + + # ============================================================================ + # Customize Segment Traits Request Generation + # ============================================================================ + def test_user_admin_build_alter_request_replace_existing_segment_traits(self): + user_admin = UserAdmin( + generate_requests_only=True, + replace_existing_segment_traits=TestCommonConstants.TEST_USER_REPLACE_SEGMENT_TRAITS, + ) + result = user_admin.alter( + "squidwrd", traits=TestCommonConstants.TEST_ALTER_USER_CSDATA_REQUEST_TRAITS + ) + self.assertEqual( + result, TestCommonConstants.TEST_ALTER_USER_REQUEST_REPLACE_SEGMENTS_XML + ) + + def test_user_admin_build_alter_request_update_existing_segment_traits(self): + user_admin = UserAdmin( + generate_requests_only=True, + update_existing_segment_traits=TestCommonConstants.TEST_USER_ADDITIONAL_SEGMENT_TRAITS, + ) + result = user_admin.alter( + "squidwrd", + traits=TestCommonConstants.TEST_ALTER_USER_CSDATA_AND_OMVS_REQUEST_TRAITS, + ) + self.assertEqual( + result, + TestCommonConstants.TEST_ALTER_USER_REQUEST_UPDATE_SEGMENTS_XML, + ) + + # ============================================================================ + # Customize Segment Traits Result Parsing + # ============================================================================ + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_user_admin_can_parse_extract_user_base_omvs_csdata_success_xml( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin( + update_existing_segment_traits=TestCommonConstants.TEST_USER_REPLACE_SEGMENT_TRAITS + ) + call_racf_mock.return_value = ( + TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_XML + ) + self.assertEqual( + user_admin.extract("squidwrd", segments=["omvs", "csdata"]), + TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_DICTIONARY, + ) diff --git a/tests/common/test_null_response_error.py b/tests/common/test_null_response_error.py index f38cb354..dea1e016 100644 --- a/tests/common/test_null_response_error.py +++ b/tests/common/test_null_response_error.py @@ -1,10 +1,11 @@ -"""Test general resource profile result parser.""" +"""Test null response error raising.""" import unittest from unittest.mock import Mock, patch import __init__ +import tests.common.test_common_constants as TestCommonConstants from pyracf import NullResponseError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 @@ -18,17 +19,39 @@ class TestNullResponseError(unittest.TestCase): IRRSMO00.__init__ = Mock(return_value=None) user_admin = UserAdmin() - def test_null_response_error_thrown_on_null_response( + def test_null_response_error_thrown_on_precheck_error( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = "" + call_racf_mock.return_value = [8, 200, 16] with self.assertRaises(NullResponseError) as exception: self.user_admin.set_password("TESTUSER", "Testpass") self.assertEqual( exception.exception.message, - "(NullResponseError) Security request made to IRRSMO00 failed." - + "\n\nCheck to see if proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class.", + TestCommonConstants.TEST_NULL_RESPONSE_PRECHECK_TEXT, + ) + + def test_null_response_error_thrown_on_surrogat_error( + self, + call_racf_mock: Mock, + ): + call_racf_mock.return_value = [8, 200, 8] + self.user_admin.set_running_userid("ESWIFT") + with self.assertRaises(NullResponseError) as exception: + self.user_admin.add("squidwrd") + self.assertEqual( + exception.exception.message, + TestCommonConstants.TEST_NULL_RESPONSE_SURROGAT_TEXT, + ) + + def test_null_response_error_thrown_on_miscellaneous_error( + self, + call_racf_mock: Mock, + ): + call_racf_mock.return_value = [8, 2000, 20] + with self.assertRaises(NullResponseError) as exception: + self.user_admin.add("squidwrd") + self.assertEqual( + exception.exception.message, + TestCommonConstants.TEST_NULL_RESPONSE_GENERIC_TEXT, ) diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py new file mode 100644 index 00000000..8c062559 --- /dev/null +++ b/tests/common/test_run_as_userid.py @@ -0,0 +1,162 @@ +"""Test functions for run as userid.""" + +import contextlib +import io +import re +import unittest +from unittest.mock import Mock, patch + +import __init__ + +import tests.common.test_common_constants as TestCommonConstants +from pyracf import ImproperUserIDError, ResourceAdmin, UserAdmin +from pyracf.common.irrsmo00 import IRRSMO00 + +# Resolves F401 +__init__ + + +class TestRunAsUserID(unittest.TestCase): + maxDiff = None + IRRSMO00.__init__ = Mock(return_value=None) + ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_set_run_as_userid_on_object_creation( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, run_as_userid="ESWIFT") + call_racf_mock.side_effect = [ + TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + user_admin.alter( + "squidwrd", + traits=TestCommonConstants.TEST_ALTER_USER_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, TestCommonConstants.TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG + ) + + def test_set_run_as_userid_on_object_creation_raises_improper_userid_error(self): + userid = "ESWIFTTEST" + with self.assertRaises(ImproperUserIDError) as exception: + UserAdmin(debug=True, run_as_userid=userid) + self.assertEqual( + exception.exception.message, + "Cannot run under this userid." + + f"{userid} is not a string from 1 to 8 characters in length.", + ) + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_set_running_userid_after_object_creation( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True) + user_admin.set_running_userid("ESWIFT") + call_racf_mock.side_effect = [ + TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + user_admin.alter( + "squidwrd", + traits=TestCommonConstants.TEST_ALTER_USER_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, TestCommonConstants.TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG + ) + + def test_set_running_userid_after_object_creation_raises_improper_userid_error( + self, + ): + userid = "ESWIFTTEST" + user_admin = UserAdmin(debug=True) + with self.assertRaises(ImproperUserIDError) as exception: + user_admin.set_running_userid(userid) + self.assertEqual( + exception.exception.message, + "Cannot run under this userid." + + f"{userid} is not a string from 1 to 8 characters in length.", + ) + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_clear_running_userid( + self, + call_racf_mock: Mock, + ): + user_admin = UserAdmin(debug=True, run_as_userid="ESWIFT") + user_admin.clear_running_userid() + call_racf_mock.side_effect = [ + TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_XML, + ] + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + user_admin.alter( + "squidwrd", + traits=TestCommonConstants.TEST_ALTER_USER_REQUEST_TRAITS, + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual(success_log, TestCommonConstants.TEST_ALTER_USER_SUCCESS_LOG) + + def test_get_running_userid(self): + user_admin = UserAdmin(run_as_userid="ESWIFT") + running_user = user_admin.get_running_userid() + self.assertEqual(running_user, TestCommonConstants.TEST_RUNNING_USERID) + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_get_user_access( + self, + call_racf_mock: Mock, + ): + precheck_profile_as_squidwrd = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + precheck_profile_as_squidwrd = precheck_profile_as_squidwrd.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ READ NO", + ) + resource_admin = ResourceAdmin(debug=True, run_as_userid="ESWIFT") + call_racf_mock.return_value = precheck_profile_as_squidwrd + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + access = resource_admin.get_user_access( + "IRR.IRRSMO00.PRECHECK", "XFACILIT", "squidwrd" + ) + success_log = self.ansi_escape.sub("", stdout.getvalue()) + self.assertEqual( + success_log, + TestCommonConstants.TEST_EXTRACT_RESOURCE_PRECHECK_AS_SQUIDWRD_LOG, + ) + self.assertEqual(access, "read") + + @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") + def test_get_user_access_raises_improper_userid_error( + self, + call_racf_mock: Mock, + ): + userid = "squidwrdtest" + precheck_profile_as_squidwrd = ( + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML + ) + precheck_profile_as_squidwrd = precheck_profile_as_squidwrd.replace( + " 00 ESWIFT READ ALTER NO", + " 00 ESWIFT READ READ NO", + ) + resource_admin = ResourceAdmin(debug=True, run_as_userid="ESWIFT") + call_racf_mock.return_value = precheck_profile_as_squidwrd + with self.assertRaises(ImproperUserIDError) as exception: + resource_admin.get_user_access("IRR.IRRSMO00.PRECHECK", "XFACILIT", userid) + self.assertEqual( + exception.exception.message, + "Cannot run under this userid." + + f"{userid} is not a string from 1 to 8 characters in length.", + ) diff --git a/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log b/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log new file mode 100644 index 00000000..16f7cf33 --- /dev/null +++ b/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log @@ -0,0 +1,171 @@ + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.get_user_access() + + +{} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.get_user_access() + + + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.get_user_access() + + + + + + + 0 + 0 + 0 + RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) + CLASS NAME + ----- ---- + XFACILIT IRR.IRRSMO00.PRECHECK + + GROUP CLASS NAME + ----- ----- ---- + GXFACILI + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ READ NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.get_user_access() + + +{ + "securityResult": { + "resource": { + "name": "IRR.IRRSMO00.PRECHECK", + "class": "XFACILIT", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) ", + "messages": [ + "CLASS NAME", + "----- ----", + "XFACILIT IRR.IRRSMO00.PRECHECK", + " ", + "GROUP CLASS NAME", + "----- ----- ----", + "GXFACILI", + " ", + "LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING", + "----- -------- ---------------- ----------- -------", + " 00 ESWIFT READ READ NO", + " ", + "INSTALLATION DATA", + "-----------------", + "NONE", + " ", + "APPLICATION DATA", + "----------------", + "NONE", + " ", + "AUDITING", + "--------", + "FAILURES(READ)", + " ", + "NOTIFY", + "------", + "NO USER TO BE NOTIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "SQUIDWRD" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + ResourceAdmin.get_user_access() + + +{ + "securityResult": { + "resource": { + "name": "IRR.IRRSMO00.PRECHECK", + "class": "XFACILIT", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) ", + "profiles": [ + { + "base": { + "class": "xfacilit", + "name": "irr.irrsmo00.precheck", + "groupClassName": "gxfacili", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "yourAccess": "read", + "warning": null, + "installationData": null, + "applicationData": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "SQUIDWRD" + } +} + diff --git a/tests/test_runner.py b/tests/test_runner.py index 7c55803d..0da586ef 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -8,9 +8,11 @@ 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_customize_segment_traits import TestCustomizeSegmentTraits from tests.common.test_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger from tests.common.test_null_response_error import TestNullResponseError +from tests.common.test_run_as_userid import TestRunAsUserID from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -61,9 +63,11 @@ def __test_suite() -> unittest.TestSuite: TestAccessResultParser, TestAccessRequestBuilder, TestAccessDebugLogging, + TestCustomizeSegmentTraits, TestInstallPrecheckScript, TestLogger, TestNullResponseError, + TestRunAsUserID, TestConnectionResultParser, TestConnectionRequestBuilder, TestConnectionSetters, diff --git a/tests/user/test_user_constants.py b/tests/user/test_user_constants.py index d5d4cb21..7188a912 100644 --- a/tests/user/test_user_constants.py +++ b/tests/user/test_user_constants.py @@ -435,43 +435,3 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "extract_user_base_omvs_success.log" ) TEST_EXTRACT_USER_BASE_OMVS_ERROR_LOG = get_sample("extract_user_base_omvs_error.log") - -# ============================================================================ -# Customize Segment Traits -# ============================================================================ - -# Alter User Traits -TEST_ALTER_USER_CSDATA_AND_OMVS_REQUEST_TRAITS = { - "base:special": False, - "omvs:home_directory": "/u/clarinet", - "omvs:default_shell": False, - "csdata:tstcsfld": "testval", -} -TEST_ALTER_USER_CSDATA_REQUEST_TRAITS = { - "base:special": False, - "csdata:tstcsfld": "testval", -} - -# Valid Segment Traits Updates -TEST_USER_REPLACE_SEGMENT_TRAITS = { - "base": {"base:special": "alt:special"}, - "csdata": {"csdata:tstcsfld": "tstcsfld"}, -} - -TEST_USER_ADDITIONAL_SEGMENT_TRAITS = {"csdata": {"csdata:tstcsfld": "tstcsfld"}} - -# Alter User Requests -TEST_ALTER_USER_REQUEST_REPLACE_SEGMENTS_XML = get_sample( - "alter_user_request_replace_segments.xml" -) -TEST_ALTER_USER_REQUEST_UPDATE_SEGMENTS_XML = get_sample( - "alter_user_request_update_segments.xml" -) - -# Extract User Results -TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_XML = get_sample( - "extract_user_result_base_omvs_csdata_success.xml" -) -TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_DICTIONARY = get_sample( - "extract_user_result_base_omvs_csdata_success.json" -) diff --git a/tests/user/test_user_request_builder.py b/tests/user/test_user_request_builder.py index 7c7a15f5..655ac77a 100644 --- a/tests/user/test_user_request_builder.py +++ b/tests/user/test_user_request_builder.py @@ -92,35 +92,6 @@ def test_user_admin_build_add_user_request_passphrase_and_password_redacted( self.assertNotIn(self.test_password, result.decode("utf-8")) self.assertNotIn(self.test_passphrase, result.decode("utf-8")) - # ============================================================================ - # Customize Segment Traits - # ============================================================================ - def test_user_admin_build_alter_request_replace_existing_segment_traits(self): - user_admin = UserAdmin( - generate_requests_only=True, - replace_existing_segment_traits=TestUserConstants.TEST_USER_REPLACE_SEGMENT_TRAITS, - ) - result = user_admin.alter( - "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_CSDATA_REQUEST_TRAITS - ) - self.assertEqual( - result, TestUserConstants.TEST_ALTER_USER_REQUEST_REPLACE_SEGMENTS_XML - ) - - def test_user_admin_build_alter_request_update_existing_segment_traits(self): - user_admin = UserAdmin( - generate_requests_only=True, - update_existing_segment_traits=TestUserConstants.TEST_USER_ADDITIONAL_SEGMENT_TRAITS, - ) - result = user_admin.alter( - "squidwrd", - traits=TestUserConstants.TEST_ALTER_USER_CSDATA_AND_OMVS_REQUEST_TRAITS, - ) - self.assertEqual( - result, - TestUserConstants.TEST_ALTER_USER_REQUEST_UPDATE_SEGMENTS_XML, - ) - # ============================================================================ # Request Builder Errors # ============================================================================ diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index c7789f98..36457c8c 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -443,24 +443,6 @@ def test_user_admin_can_parse_delete_user_error_xml( TestUserConstants.TEST_DELETE_USER_RESULT_ERROR_DICTIONARY, ) - # ============================================================================ - # Extract User with Customized Segment Traits - # ============================================================================ - def test_user_admin_can_parse_extract_user_base_omvs_csdata_success_xml( - self, - call_racf_mock: Mock, - ): - user_admin = UserAdmin( - update_existing_segment_traits=TestUserConstants.TEST_USER_REPLACE_SEGMENT_TRAITS - ) - call_racf_mock.return_value = ( - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_XML - ) - self.assertEqual( - user_admin.extract("squidwrd", segments=["omvs", "csdata"]), - TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_DICTIONARY, - ) - # ============================================================================ # Add Additional Secrets # ============================================================================ diff --git a/tests/user/user_log_samples/alter_user_success_as_eswift.log b/tests/user/user_log_samples/alter_user_success_as_eswift.log new file mode 100644 index 00000000..c414e4f8 --- /dev/null +++ b/tests/user/user_log_samples/alter_user_success_as_eswift.log @@ -0,0 +1,263 @@ + + [pyRACF:Debug] + Request Dictionary + UserAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + UserAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + UserAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTUSER SQUIDWRD + USER=SQUIDWRD NAME=SQUIDWARD OWNER=LEONARD CREATED=23.094 + DEFAULT-GROUP=SYS1 PASSDATE=00.000 PASS-INTERVAL=186 PHRASEDATE=N/A + ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + LAST-ACCESS=23.094/12:55:37 + CLASS AUTHORIZATIONS=NONE + NO-INSTALLATION-DATA + NO-MODEL-NAME + LOGON ALLOWED (DAYS) (TIME) + --------------------------------------------- + ANYDAY ANYTIME + GROUP=SYS1 AUTH=USE CONNECT-OWNER=LEONARD CONNECT-DATE=23.094 + CONNECTS= 00 UACC=NONE LAST-CONNECT=UNKNOWN + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + SECURITY-LEVEL=NONE SPECIFIED + CATEGORY-AUTHORIZATION + NONE SPECIFIED + SECURITY-LABEL=NONE SPECIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + UserAdmin.alter() + + +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "listdata", + "requestId": "UserRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTUSER SQUIDWRD ", + "messages": [ + "USER=SQUIDWRD NAME=SQUIDWARD OWNER=LEONARD CREATED=23.094", + " DEFAULT-GROUP=SYS1 PASSDATE=00.000 PASS-INTERVAL=186 PHRASEDATE=N/A", + " ATTRIBUTES=NONE", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LAST-ACCESS=23.094/12:55:37", + " CLASS AUTHORIZATIONS=NONE", + " NO-INSTALLATION-DATA", + " NO-MODEL-NAME", + " LOGON ALLOWED (DAYS) (TIME)", + " ---------------------------------------------", + " ANYDAY ANYTIME", + " GROUP=SYS1 AUTH=USE CONNECT-OWNER=LEONARD CONNECT-DATE=23.094", + " CONNECTS= 00 UACC=NONE LAST-CONNECT=UNKNOWN", + " CONNECT ATTRIBUTES=NONE", + " REVOKE DATE=NONE RESUME DATE=NONE", + "SECURITY-LEVEL=NONE SPECIFIED", + "CATEGORY-AUTHORIZATION", + " NONE SPECIFIED", + "SECURITY-LABEL=NONE SPECIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "ESWIFT" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + UserAdmin.alter() + + +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "listdata", + "requestId": "UserRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTUSER SQUIDWRD ", + "profiles": [ + { + "base": { + "user": "squidwrd", + "name": "squidward", + "owner": "leonard", + "created": "4/4/2023", + "defaultGroup": "sys1", + "passwordDate": null, + "passwordInterval": 186, + "passphraseDate": null, + "attributes": [], + "revokeDate": null, + "resumeDate": null, + "lastAccess": "4/4/2023 12:55 PM", + "classAuthorizations": [], + "logonAllowedDays": "anyday", + "logonAllowedTime": "anytime", + "groups": { + "SYS1": { + "auth": "use", + "connectOwner": "leonard", + "connectDate": "4/4/2023", + "connects": 0, + "uacc": null, + "lastConnect": null, + "connectAttributes": [], + "revokeDate": null, + "resumeDate": null + } + }, + "securityLevel": null, + "categoryAuthorization": null, + "securityLabel": null + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "ESWIFT" + } +} + + + [pyRACF:Debug] + Request Dictionary + UserAdmin.alter() + + +{ + "base": { + "base:special": { + "value": false, + "operation": "delete" + } + }, + "omvs": { + "omvs:home_directory": { + "value": "/u/clarinet", + "operation": null + }, + "omvs:default_shell": { + "value": false, + "operation": "delete" + } + } +} + + + [pyRACF:Debug] + Request XML + UserAdmin.alter() + + + + + + + + + /u/clarinet + + + + + + + [pyRACF:Debug] + Result XML + UserAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM ) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + UserAdmin.alter() + + +{ + "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 NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM )" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "ESWIFT" + } +} + From a6e927dd6abc535431f622fbdce2f1e5a69bcae7 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Sat, 25 Nov 2023 21:55:15 -0500 Subject: [PATCH 07/30] Minor changes for doc -Update type hints and error message to bring in line with doc Signed-off-by: Elijah Swift --- pyracf/common/null_response_error.py | 2 +- pyracf/common/security_admin.py | 12 ++++++------ tests/common/test_common_constants.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py index e8d087cc..4a5171bb 100644 --- a/pyracf/common/null_response_error.py +++ b/pyracf/common/null_response_error.py @@ -29,7 +29,7 @@ def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None ): self.message += ( "\n\nCheck to see if proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least ALTER" + + "For the `run_as_userid` feature, you must have at least UPDATE" + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." ) else: diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index a28e990d..48064acf 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -88,7 +88,7 @@ def __init__( # ============================================================================ # Run as Other User ID # ============================================================================ - def set_running_userid(self, new_userid: Union[str, None]): + def set_running_userid(self, new_userid: Union[str, None]) -> None: if new_userid is None: self.__running_userid = new_userid return @@ -101,16 +101,16 @@ def set_running_userid(self, new_userid: Union[str, None]): return raise ImproperUserIDError(new_userid) - def clear_running_userid(self): + def clear_running_userid(self) -> None: self.__running_userid = None - def get_running_userid(self): + def get_running_userid(self) -> None: return self.__running_userid # ============================================================================ # Customize Segment Traits # ============================================================================ - def __update_valid_segment_traits(self, update_valid_segment_traits: dict): + def __update_valid_segment_traits(self, update_valid_segment_traits: dict) -> None: """Update fields to valid segment traits dictionary.""" for segment in update_valid_segment_traits: if segment in self._valid_segment_traits: @@ -122,14 +122,14 @@ def __update_valid_segment_traits(self, update_valid_segment_traits: dict): segment ] - def __replace_valid_segment_traits(self, new_valid_segment_traits: dict): + def __replace_valid_segment_traits(self, new_valid_segment_traits: dict) -> None: """Replace field data in valid segment traits dictionary""" self._valid_segment_traits = new_valid_segment_traits # ============================================================================ # Secrets Redaction # ============================================================================ - def __add_additional_secret_traits(self, additional_secret_traits: list): + def __add_additional_secret_traits(self, additional_secret_traits: list) -> None: """Add additional fields to be redacted in logger output.""" for secret in additional_secret_traits: if secret in self.__secret_traits: diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 8324a797..6e39a807 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -127,7 +127,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_SURROGAT_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." + "\n\nCheck to see if proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least ALTER" + + "For the `run_as_userid` feature, you must have at least UPDATE" + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." ) From 723a381676352f6834dceac060751e0dbf1b1cae Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 29 Nov 2023 09:51:23 -0500 Subject: [PATCH 08/30] Refactoring and Standardization Changes Change UserID to UserId refactor call_racf change error text strings Signed-off-by: Elijah Swift --- pyracf/__init__.py | 2 +- pyracf/common/improper_userid_error.py | 10 +++++----- pyracf/common/irrsmo00.py | 19 ++++++++----------- pyracf/common/null_response_error.py | 8 ++++++-- pyracf/common/security_admin.py | 4 ++-- tests/access/test_access_result_parser.py | 2 +- tests/common/test_common_constants.py | 9 ++++++--- tests/common/test_run_as_userid.py | 22 +++++++++++----------- tests/test_runner.py | 4 ++-- 9 files changed, 42 insertions(+), 38 deletions(-) diff --git a/pyracf/__init__.py b/pyracf/__init__.py index 13da5008..d6d547aa 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,7 +2,7 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError -from .common.improper_userid_error import ImproperUserIDError +from .common.improper_userid_error import ImproperUserIdError from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError from .common.segment_error import SegmentError diff --git a/pyracf/common/improper_userid_error.py b/pyracf/common/improper_userid_error.py index 60e24f6e..d0dd67a2 100644 --- a/pyracf/common/improper_userid_error.py +++ b/pyracf/common/improper_userid_error.py @@ -1,16 +1,16 @@ """Exception to use when attempting to set an invalid userid for pyRACF to run under.""" -class ImproperUserIDError(Exception): +class ImproperUserIdError(Exception): """ - Raised when pyRACF would attempt to establish itself to - run under a passed 'userid' that is not a string of 1 to 8 characters. + Raised when pyRACF would attempt to run as a userid + that is not a string between 1 to 8 characters in length. """ def __init__(self, userid: str) -> None: self.message = ( - "Cannot run under this userid." - + f"{userid} is not a string from 1 to 8 characters in length." + f"Unable to run as userid '{userid}'. Userid must " + + "be a string value between 1 to 8 characters in length." ) def __str__(self) -> str: diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index b789d03f..ab92e058 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -24,24 +24,21 @@ def call_racf( self, request_xml: bytes, precheck: bool = False, - running_userid: Union[str, bool] = None, + run_as_userid: Union[str, None] = None, ) -> str: """Make request to call_irrsmo00 in the cpyracf Python extension.""" options = 15 if precheck else 13 - if running_userid is None: - response = call_irrsmo00( - xml_str=request_xml, - xml_len=len(request_xml), - opts=options, - userid="".encode("cp1047"), - userid_len=0, - ).decode("cp1047") + userid = b"" + userid_length = 0 + if run_as_userid: + userid = run_as_userid.encode("cp1047") + userid_length = len(run_as_userid) response = call_irrsmo00( xml_str=request_xml, xml_len=len(request_xml), opts=options, - userid=running_userid.encode("cp1047"), - userid_len=len(running_userid), + userid=userid, + userid_len=userid_length, ).decode("cp1047") if response[0] == "": return response[1:3] diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py index 4a5171bb..89292f47 100644 --- a/pyracf/common/null_response_error.py +++ b/pyracf/common/null_response_error.py @@ -12,13 +12,17 @@ def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None self.saf_return_code = xml_str[0] self.racf_return_code = xml_str[1] self.racf_reason_code = xml_str[2] + self.message += ( + f"\nSAF Return Code: {self.saf_return_code} / RACF Return Code:" + + f" {self.racf_return_code} / RACF Reason Code: {self.racf_reason_code}" + ) if ( (self.saf_return_code == 8) and (self.racf_return_code == 200) and (self.racf_reason_code == 16) ): self.message += ( - "\n\nCheck to see if proper RACF permissions are in place.\n" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." ) @@ -28,7 +32,7 @@ def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None and (self.racf_reason_code == 8) ): self.message += ( - "\n\nCheck to see if proper RACF permissions are in place.\n" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." ) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 48064acf..9b79a525 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -5,7 +5,7 @@ from datetime import datetime from typing import Any, List, Tuple, Union -from .improper_userid_error import ImproperUserIDError +from .improper_userid_error import ImproperUserIdError from .irrsmo00 import IRRSMO00 from .logger import Logger from .null_response_error import NullResponseError @@ -99,7 +99,7 @@ def set_running_userid(self, new_userid: Union[str, None]) -> None: ): self.__running_userid = new_userid.upper() return - raise ImproperUserIDError(new_userid) + raise ImproperUserIdError(new_userid) def clear_running_userid(self) -> None: self.__running_userid = None diff --git a/tests/access/test_access_result_parser.py b/tests/access/test_access_result_parser.py index b8d15926..3adb4e9c 100644 --- a/tests/access/test_access_result_parser.py +++ b/tests/access/test_access_result_parser.py @@ -36,7 +36,7 @@ def test_access_admin_can_parse_permit_access_success_xml( TestAccessConstants.TEST_PERMIT_ACCESS_RESULT_SUCCESS_DICTIONARY, ) - # Error, UserID MCGINLEY not defined to RACF + # Error, UserId MCGINLEY not defined to RACF def test_access_admin_can_parse_permit_access_error_xml( self, call_racf_mock: Mock, diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 6e39a807..243299eb 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -91,7 +91,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) # ============================================================================ -# Run As UserID +# Run As UserId # ============================================================================ TEST_RUNNING_USERID = "ESWIFT" @@ -119,20 +119,23 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_PRECHECK_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "\nSAF Return Code: 8 / RACF Return Code: 200 / RACF Reason Code: 16" + + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." ) TEST_NULL_RESPONSE_SURROGAT_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\n\nCheck to see if proper RACF permissions are in place.\n" + + "\nSAF Return Code: 8 / RACF Return Code: 200 / RACF Reason Code: 8" + + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." ) TEST_NULL_RESPONSE_GENERIC_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." + + "\nSAF Return Code: 8 / RACF Return Code: 2000 / RACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" + " about this error.\n" diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py index 8c062559..fb1fbb83 100644 --- a/tests/common/test_run_as_userid.py +++ b/tests/common/test_run_as_userid.py @@ -9,14 +9,14 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import ImproperUserIDError, ResourceAdmin, UserAdmin +from pyracf import ImproperUserIdError, ResourceAdmin, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 __init__ -class TestRunAsUserID(unittest.TestCase): +class TestRunAsUserId(unittest.TestCase): maxDiff = None IRRSMO00.__init__ = Mock(return_value=None) ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") @@ -44,12 +44,12 @@ def test_set_run_as_userid_on_object_creation( def test_set_run_as_userid_on_object_creation_raises_improper_userid_error(self): userid = "ESWIFTTEST" - with self.assertRaises(ImproperUserIDError) as exception: + with self.assertRaises(ImproperUserIdError) as exception: UserAdmin(debug=True, run_as_userid=userid) self.assertEqual( exception.exception.message, - "Cannot run under this userid." - + f"{userid} is not a string from 1 to 8 characters in length.", + f"Unable to run as userid '{userid}'. Userid must " + + "be a string value between 1 to 8 characters in length.", ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -79,12 +79,12 @@ def test_set_running_userid_after_object_creation_raises_improper_userid_error( ): userid = "ESWIFTTEST" user_admin = UserAdmin(debug=True) - with self.assertRaises(ImproperUserIDError) as exception: + with self.assertRaises(ImproperUserIdError) as exception: user_admin.set_running_userid(userid) self.assertEqual( exception.exception.message, - "Cannot run under this userid." - + f"{userid} is not a string from 1 to 8 characters in length.", + f"Unable to run as userid '{userid}'. Userid must " + + "be a string value between 1 to 8 characters in length.", ) @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") @@ -153,10 +153,10 @@ def test_get_user_access_raises_improper_userid_error( ) resource_admin = ResourceAdmin(debug=True, run_as_userid="ESWIFT") call_racf_mock.return_value = precheck_profile_as_squidwrd - with self.assertRaises(ImproperUserIDError) as exception: + with self.assertRaises(ImproperUserIdError) as exception: resource_admin.get_user_access("IRR.IRRSMO00.PRECHECK", "XFACILIT", userid) self.assertEqual( exception.exception.message, - "Cannot run under this userid." - + f"{userid} is not a string from 1 to 8 characters in length.", + f"Unable to run as userid '{userid}'. Userid must " + + "be a string value between 1 to 8 characters in length.", ) diff --git a/tests/test_runner.py b/tests/test_runner.py index 0da586ef..1a5850f3 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -12,7 +12,7 @@ from tests.common.test_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger from tests.common.test_null_response_error import TestNullResponseError -from tests.common.test_run_as_userid import TestRunAsUserID +from tests.common.test_run_as_userid import TestRunAsUserId from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -67,7 +67,7 @@ def __test_suite() -> unittest.TestSuite: TestInstallPrecheckScript, TestLogger, TestNullResponseError, - TestRunAsUserID, + TestRunAsUserId, TestConnectionResultParser, TestConnectionRequestBuilder, TestConnectionSetters, From a1b9be19f5bfe4619eb83b5d512de89ec2ac81f2 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 29 Nov 2023 16:24:30 -0500 Subject: [PATCH 09/30] Updates -Change ImproperUserIdError to UserIdError -Move security request error restructuring to security result -doc and minor updates. Signed-off-by: Elijah Swift --- pyracf/__init__.py | 2 +- pyracf/common/improper_userid_error.py | 2 +- pyracf/common/null_response_error.py | 4 +- pyracf/common/security_admin.py | 10 ++--- pyracf/common/security_request_error.py | 41 +------------------ pyracf/common/security_result.py | 40 +++++++++++++++++- tests/common/test_common_constants.py | 6 +-- tests/common/test_install_precheck_script.py | 2 +- tests/common/test_run_as_userid.py | 8 ++-- tests/group/test_group_result_parser.py | 1 - ...extract_resource_result_precheck_error.xml | 2 +- 11 files changed, 59 insertions(+), 59 deletions(-) diff --git a/pyracf/__init__.py b/pyracf/__init__.py index d6d547aa..c93e4dc2 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,7 +2,7 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError -from .common.improper_userid_error import ImproperUserIdError +from .common.improper_userid_error import UserIdError from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError from .common.segment_error import SegmentError diff --git a/pyracf/common/improper_userid_error.py b/pyracf/common/improper_userid_error.py index d0dd67a2..304c868a 100644 --- a/pyracf/common/improper_userid_error.py +++ b/pyracf/common/improper_userid_error.py @@ -1,7 +1,7 @@ """Exception to use when attempting to set an invalid userid for pyRACF to run under.""" -class ImproperUserIdError(Exception): +class UserIdError(Exception): """ Raised when pyRACF would attempt to run as a userid that is not a string between 1 to 8 characters in length. diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py index 89292f47..1d4534f5 100644 --- a/pyracf/common/null_response_error.py +++ b/pyracf/common/null_response_error.py @@ -13,8 +13,8 @@ def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None self.racf_return_code = xml_str[1] self.racf_reason_code = xml_str[2] self.message += ( - f"\nSAF Return Code: {self.saf_return_code} / RACF Return Code:" - + f" {self.racf_return_code} / RACF Reason Code: {self.racf_reason_code}" + f"\nSAF Return Code: {self.saf_return_code} \nRACF Return Code:" + + f" {self.racf_return_code} \nRACF Reason Code: {self.racf_reason_code}" ) if ( (self.saf_return_code == 8) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 9b79a525..4ca303c7 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -5,7 +5,7 @@ from datetime import datetime from typing import Any, List, Tuple, Union -from .improper_userid_error import ImproperUserIdError +from .improper_userid_error import UserIdError from .irrsmo00 import IRRSMO00 from .logger import Logger from .null_response_error import NullResponseError @@ -22,7 +22,7 @@ class SecurityAdmin: _valid_segment_traits = {} _extracted_key_value_pair_segment_traits_map = {} _case_sensitive_extracted_values = [] - __running_userid = False + __running_userid = None __logger = Logger() def __init__( @@ -99,7 +99,7 @@ def set_running_userid(self, new_userid: Union[str, None]) -> None: ): self.__running_userid = new_userid.upper() return - raise ImproperUserIdError(new_userid) + raise UserIdError(new_userid) def clear_running_userid(self) -> None: self.__running_userid = None @@ -209,7 +209,7 @@ def _make_request( # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. self.__logger.log_xml("Result XML", result_xml) - results = SecurityResult(result_xml, self.get_running_userid()) + results = SecurityResult(result_xml, request_xml, self.get_running_userid()) if self.__debug: # No need to redact anything here since the result dictionary # already has secrets redacted when it is built. @@ -223,7 +223,7 @@ def _make_request( # up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. - raise SecurityRequestError(result_dictionary, request_xml) + raise SecurityRequestError(result_dictionary) return result_dictionary def _to_steps(self, results: Union[List[dict], dict, bytes]) -> Union[dict, bytes]: diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 4584d0ca..56d30d48 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -6,10 +6,9 @@ class SecurityRequestError(Exception): Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. """ - def __init__(self, result: dict, request_xml: str) -> None: + def __init__(self, result: dict) -> None: self.message = "Security request made to IRRSMO00 failed." - self.request_xml = request_xml - self.result = self.__check_result_type(result) + self.result = result self.message += ( "\n\nSee results dictionary " + f"'{self.__class__.__name__}.result' for more details." @@ -19,42 +18,6 @@ def __init__(self, result: dict, request_xml: str) -> None: def __str__(self) -> str: return self.message - def __check_result_type(self, result: dict) -> dict: - not_profiles = ["returnCode", "reasonCode", "runningUserid"] - for profile_type in result["securityResult"]: - if profile_type in not_profiles: - continue - if "error" in result["securityResult"][profile_type]: - return self.__organize_smo_error(result, profile_type) - return result - - def __organize_smo_error(self, result: dict, profile_type: str) -> dict: - command_result = {} - command_result["image"] = self.request_xml.decode("utf-8") - command_result["safReturnCode"] = result["securityResult"][profile_type][ - "error" - ]["errorFunction"] - command_result["returnCode"] = result["securityResult"][profile_type]["error"][ - "errorCode" - ] - command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ - "errorReason" - ] - error_offset = result["securityResult"][profile_type]["error"]["errorOffset"] - error_text = result["securityResult"][profile_type]["error"]["textInError"] - error_message = ( - result["securityResult"][profile_type]["error"]["errorMessage"] - + "\n" - + f"Text in Error: '{error_text}'\n" - + f"Approximate Offset of Text in Error: '{error_offset}'\n" - + "Please note that for any text in error," - + " redacted values may skew offset calculations." - ) - command_result["messages"] = [error_message] - result["securityResult"][profile_type]["commands"] = [command_result] - del result["securityResult"][profile_type]["error"] - return result - def contains_error_message( self, security_definition_tag: str, error_message_id: str ): diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index b474cf83..b57b22f7 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -11,11 +11,13 @@ class SecurityResult: """Generic Security Result Parser.""" def __init__( - self, result_xml: str, running_userid: Union[str, None] = None + self, result_xml: str, request_xml: str, running_userid: Union[str, None] = None ) -> None: + self.__request_xml = request_xml self.__result = XMLParser.fromstring(result_xml) self.__result_dictionary = {"securityResult": {}} self.__extract_results() + self.__result_dictionary = self.__check_result_type(self.__result_dictionary) if running_userid is not None: self.__result_dictionary["securityResult"]["runningUserid"] = running_userid @@ -61,6 +63,42 @@ def __extract_info(self) -> None: info.append(item.text) self.__definition.remove(item) + def __check_result_type(self, result: dict) -> dict: + not_profiles = ["returnCode", "reasonCode", "runningUserid"] + for profile_type in result["securityResult"]: + if profile_type in not_profiles: + continue + if "error" in result["securityResult"][profile_type]: + return self.__organize_smo_error(result, profile_type) + return result + + def __organize_smo_error(self, result: dict, profile_type: str) -> dict: + command_result = {} + command_result["image"] = self.__request_xml.decode("utf-8") + command_result["safReturnCode"] = result["securityResult"][profile_type][ + "error" + ]["errorFunction"] + command_result["returnCode"] = result["securityResult"][profile_type]["error"][ + "errorCode" + ] + command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ + "errorReason" + ] + error_offset = result["securityResult"][profile_type]["error"]["errorOffset"] + error_text = result["securityResult"][profile_type]["error"]["textInError"] + error_message = ( + result["securityResult"][profile_type]["error"]["errorMessage"] + + "\n" + + f"Text in Error: '{error_text}'\n" + + f"Approximate Offset of Text in Error: '{error_offset}'\n" + + "Please note that for any text in error," + + " redacted values may skew offset calculations." + ) + command_result["messages"] = [error_message] + result["securityResult"][profile_type]["commands"] = [command_result] + del result["securityResult"][profile_type]["error"] + return result + def __extract_commands(self, filter_out_extra_messages: bool) -> None: """Extract commands section from XML into a list.""" self.__definition_dictionary["commands"] = [] diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 243299eb..9872bf84 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -119,7 +119,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_PRECHECK_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 / RACF Return Code: 200 / RACF Reason Code: 16" + + "\nSAF Return Code: 8 \nRACF Return Code: 200 \nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." @@ -127,7 +127,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_SURROGAT_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 / RACF Return Code: 200 / RACF Reason Code: 8" + + "\nSAF Return Code: 8 \nRACF Return Code: 200 \nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." @@ -135,7 +135,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_GENERIC_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 / RACF Return Code: 2000 / RACF Reason Code: 20" + + "\nSAF Return Code: 8 \nRACF Return Code: 2000 \nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" + " about this error.\n" diff --git a/tests/common/test_install_precheck_script.py b/tests/common/test_install_precheck_script.py index f599eb3c..35efc2be 100644 --- a/tests/common/test_install_precheck_script.py +++ b/tests/common/test_install_precheck_script.py @@ -1,4 +1,4 @@ -"""Test password sanitization in resource debug logging.""" +"""Test included script to initialize and evaluate precheck permissions.""" import contextlib import io diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py index fb1fbb83..caf1cfa2 100644 --- a/tests/common/test_run_as_userid.py +++ b/tests/common/test_run_as_userid.py @@ -9,7 +9,7 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import ImproperUserIdError, ResourceAdmin, UserAdmin +from pyracf import ResourceAdmin, UserAdmin, UserIdError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -44,7 +44,7 @@ def test_set_run_as_userid_on_object_creation( def test_set_run_as_userid_on_object_creation_raises_improper_userid_error(self): userid = "ESWIFTTEST" - with self.assertRaises(ImproperUserIdError) as exception: + with self.assertRaises(UserIdError) as exception: UserAdmin(debug=True, run_as_userid=userid) self.assertEqual( exception.exception.message, @@ -79,7 +79,7 @@ def test_set_running_userid_after_object_creation_raises_improper_userid_error( ): userid = "ESWIFTTEST" user_admin = UserAdmin(debug=True) - with self.assertRaises(ImproperUserIdError) as exception: + with self.assertRaises(UserIdError) as exception: user_admin.set_running_userid(userid) self.assertEqual( exception.exception.message, @@ -153,7 +153,7 @@ def test_get_user_access_raises_improper_userid_error( ) resource_admin = ResourceAdmin(debug=True, run_as_userid="ESWIFT") call_racf_mock.return_value = precheck_profile_as_squidwrd - with self.assertRaises(ImproperUserIdError) as exception: + with self.assertRaises(UserIdError) as exception: resource_admin.get_user_access("IRR.IRRSMO00.PRECHECK", "XFACILIT", userid) self.assertEqual( exception.exception.message, diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index bbe78ba8..b02441da 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -75,7 +75,6 @@ def test_group_admin_can_parse_add_group_error_xml( self.group_admin.add( "TESTGRPP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ) - print(exception.exception.result) self.assertEqual( exception.exception.result, TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BAD_ATTRIBUTE_ERROR_DICTIONARY, diff --git a/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml b/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml index 21315a99..9621521e 100644 --- a/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml +++ b/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml @@ -6,7 +6,7 @@ 16 4 RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) - ICH13003I TESTING NOT FOUND + ICH13003I IRR.IRRSMO00.PRECHECK NOT FOUND 4 From eda318490d8664cdb79aeeab0f965cf0b05607e2 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 29 Nov 2023 16:28:38 -0500 Subject: [PATCH 10/30] Complete Transition to UserIdError Signed-off-by: Elijah Swift --- pyracf/__init__.py | 2 +- pyracf/common/security_admin.py | 2 +- pyracf/common/{improper_userid_error.py => userid_error.py} | 0 tests/common/test_run_as_userid.py | 6 +++--- 4 files changed, 5 insertions(+), 5 deletions(-) rename pyracf/common/{improper_userid_error.py => userid_error.py} (100%) diff --git a/pyracf/__init__.py b/pyracf/__init__.py index c93e4dc2..13faad7b 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,11 +2,11 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError -from .common.improper_userid_error import UserIdError from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError from .common.segment_error import SegmentError from .common.segment_trait_error import SegmentTraitError +from .common.userid_error import UserIdError from .connection.connection_admin import ConnectionAdmin from .data_set.data_set_admin import DataSetAdmin from .group.group_admin import GroupAdmin diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 4ca303c7..872417aa 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -5,7 +5,6 @@ from datetime import datetime from typing import Any, List, Tuple, Union -from .improper_userid_error import UserIdError from .irrsmo00 import IRRSMO00 from .logger import Logger from .null_response_error import NullResponseError @@ -14,6 +13,7 @@ from .security_result import SecurityResult from .segment_error import SegmentError from .segment_trait_error import SegmentTraitError +from .userid_error import UserIdError class SecurityAdmin: diff --git a/pyracf/common/improper_userid_error.py b/pyracf/common/userid_error.py similarity index 100% rename from pyracf/common/improper_userid_error.py rename to pyracf/common/userid_error.py diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py index caf1cfa2..29548f31 100644 --- a/tests/common/test_run_as_userid.py +++ b/tests/common/test_run_as_userid.py @@ -42,7 +42,7 @@ def test_set_run_as_userid_on_object_creation( success_log, TestCommonConstants.TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG ) - def test_set_run_as_userid_on_object_creation_raises_improper_userid_error(self): + def test_set_run_as_userid_on_object_creation_raises_userid_error(self): userid = "ESWIFTTEST" with self.assertRaises(UserIdError) as exception: UserAdmin(debug=True, run_as_userid=userid) @@ -74,7 +74,7 @@ def test_set_running_userid_after_object_creation( success_log, TestCommonConstants.TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG ) - def test_set_running_userid_after_object_creation_raises_improper_userid_error( + def test_set_running_userid_after_object_creation_raises_userid_error( self, ): userid = "ESWIFTTEST" @@ -139,7 +139,7 @@ def test_get_user_access( self.assertEqual(access, "read") @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") - def test_get_user_access_raises_improper_userid_error( + def test_get_user_access_raises_userid_error( self, call_racf_mock: Mock, ): From 8db616cad15a6d9385f3d664c5d61da754ffb9b5 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Thu, 30 Nov 2023 15:10:14 -0500 Subject: [PATCH 11/30] Changing error text and formatting Changing error text and formatting Signed-off-by: Elijah Swift --- pyracf/common/null_response_error.py | 11 ++-- pyracf/common/security_admin.py | 6 +- pyracf/common/security_request_error.py | 3 +- pyracf/common/security_result.py | 60 +++++++++---------- pyracf/common/userid_error.py | 5 +- tests/common/test_common_constants.py | 8 +-- .../add_group_result_error.json | 6 +- ...ract_group_result_bad_attribute_error.json | 6 +- .../extract_resource_precheck_as_squidwrd.log | 4 +- .../alter_user_success_as_eswift.log | 6 +- .../add_user_result_error.json | 6 +- ...tract_user_result_bad_attribute_error.json | 6 +- 12 files changed, 71 insertions(+), 56 deletions(-) diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py index 1d4534f5..37aeb7a1 100644 --- a/pyracf/common/null_response_error.py +++ b/pyracf/common/null_response_error.py @@ -7,14 +7,17 @@ class NullResponseError(Exception): Raised when no xml string is returned by IRRSMO00. """ - def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None: + def __init__( + self, xml_str: str, request_xml: bytes, run_as_userid: Union[str, None] = None + ) -> None: self.message = "Security request made to IRRSMO00 failed." self.saf_return_code = xml_str[0] self.racf_return_code = xml_str[1] self.racf_reason_code = xml_str[2] + self.request_xml = request_xml.decode("utf-8") self.message += ( - f"\nSAF Return Code: {self.saf_return_code} \nRACF Return Code:" - + f" {self.racf_return_code} \nRACF Reason Code: {self.racf_reason_code}" + f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" + + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" ) if ( (self.saf_return_code == 8) @@ -41,7 +44,7 @@ def __init__(self, xml_str: str, run_as_userid: Union[str, None] = None) -> None "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" + " about this error.\n" - + "(https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes)" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) self.message = f"({self.__class__.__name__}) {self.message}" diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 872417aa..e5c9b096 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -204,12 +204,12 @@ def _make_request( # When IRRSMO00 encounters some errors, it returns no XML response string. # When this happens, the c code instead surfaces the return and reason # codes which causes a NullResponseError to be raised. - raise NullResponseError(result_xml, self.get_running_userid()) + raise NullResponseError(result_xml, request_xml, self.get_running_userid()) if self.__debug: # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. self.__logger.log_xml("Result XML", result_xml) - results = SecurityResult(result_xml, request_xml, self.get_running_userid()) + results = SecurityResult(result_xml, self.get_running_userid()) if self.__debug: # No need to redact anything here since the result dictionary # already has secrets redacted when it is built. @@ -223,7 +223,7 @@ def _make_request( # up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. - raise SecurityRequestError(result_dictionary) + raise SecurityRequestError(result_dictionary, request_xml) return result_dictionary def _to_steps(self, results: Union[List[dict], dict, bytes]) -> Union[dict, bytes]: diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 56d30d48..947a176c 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -6,9 +6,10 @@ class SecurityRequestError(Exception): Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. """ - def __init__(self, result: dict) -> None: + def __init__(self, result: dict, request_xml: bytes) -> None: self.message = "Security request made to IRRSMO00 failed." self.result = result + self.request_xml = request_xml.decode("utf-8") self.message += ( "\n\nSee results dictionary " + f"'{self.__class__.__name__}.result' for more details." diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index b57b22f7..78aee2ff 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -11,15 +11,16 @@ class SecurityResult: """Generic Security Result Parser.""" def __init__( - self, result_xml: str, request_xml: str, running_userid: Union[str, None] = None + self, result_xml: str, running_userid: Union[str, None] = None ) -> None: - self.__request_xml = request_xml self.__result = XMLParser.fromstring(result_xml) self.__result_dictionary = {"securityResult": {}} self.__extract_results() self.__result_dictionary = self.__check_result_type(self.__result_dictionary) if running_userid is not None: - self.__result_dictionary["securityResult"]["runningUserid"] = running_userid + self.__result_dictionary["securityResult"][ + "runningUserid" + ] = running_userid.lower() def __extract_results(self) -> None: """Extract XML results into a dictionary.""" @@ -72,33 +73,6 @@ def __check_result_type(self, result: dict) -> dict: return self.__organize_smo_error(result, profile_type) return result - def __organize_smo_error(self, result: dict, profile_type: str) -> dict: - command_result = {} - command_result["image"] = self.__request_xml.decode("utf-8") - command_result["safReturnCode"] = result["securityResult"][profile_type][ - "error" - ]["errorFunction"] - command_result["returnCode"] = result["securityResult"][profile_type]["error"][ - "errorCode" - ] - command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ - "errorReason" - ] - error_offset = result["securityResult"][profile_type]["error"]["errorOffset"] - error_text = result["securityResult"][profile_type]["error"]["textInError"] - error_message = ( - result["securityResult"][profile_type]["error"]["errorMessage"] - + "\n" - + f"Text in Error: '{error_text}'\n" - + f"Approximate Offset of Text in Error: '{error_offset}'\n" - + "Please note that for any text in error," - + " redacted values may skew offset calculations." - ) - command_result["messages"] = [error_message] - result["securityResult"][profile_type]["commands"] = [command_result] - del result["securityResult"][profile_type]["error"] - return result - def __extract_commands(self, filter_out_extra_messages: bool) -> None: """Extract commands section from XML into a list.""" self.__definition_dictionary["commands"] = [] @@ -143,6 +117,32 @@ def __extract_error(self) -> None: except ValueError: self.__definition_dictionary["error"][item_tag] = item.text + def __organize_smo_error(self, result: dict, profile_type: str) -> dict: + command_result = {} + command_result["safReturnCode"] = result["securityResult"][profile_type][ + "error" + ]["errorFunction"] + command_result["returnCode"] = result["securityResult"][profile_type]["error"][ + "errorCode" + ] + command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ + "errorReason" + ] + command_result["errorOffset"] = result["securityResult"][profile_type]["error"][ + "errorOffset" + ] + command_result["textInError"] = result["securityResult"][profile_type]["error"][ + "textInError" + ] + command_result["messages"] = [ + result["securityResult"][profile_type]["error"]["errorMessage"], + "Please note that for any text in error," + + " redacted values may skew offset calculations.", + ] + result["securityResult"][profile_type]["commands"] = [command_result] + del result["securityResult"][profile_type]["error"] + return result + def __to_pascal_case(self, key: str) -> str: """Convert result dictionary keys to pascal case.""" match (key): diff --git a/pyracf/common/userid_error.py b/pyracf/common/userid_error.py index 304c868a..bafe4f8f 100644 --- a/pyracf/common/userid_error.py +++ b/pyracf/common/userid_error.py @@ -1,4 +1,7 @@ -"""Exception to use when attempting to set an invalid userid for pyRACF to run under.""" +""" +Exception to raise when run as userid is given a userid value +that is not a string between 1 to 8 characters in length. +""" class UserIdError(Exception): diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 9872bf84..c46638d4 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -119,7 +119,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_PRECHECK_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 \nRACF Return Code: 200 \nRACF Reason Code: 16" + + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." @@ -127,7 +127,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_SURROGAT_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 \nRACF Return Code: 200 \nRACF Reason Code: 8" + + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." @@ -135,9 +135,9 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_NULL_RESPONSE_GENERIC_TEXT = ( "(NullResponseError) Security request made to IRRSMO00 failed." - + "\nSAF Return Code: 8 \nRACF Return Code: 2000 \nRACF Reason Code: 20" + + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" + " about this error.\n" - + "(https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes)" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) diff --git a/tests/group/group_result_samples/add_group_result_error.json b/tests/group/group_result_samples/add_group_result_error.json index eb939933..24a0296c 100644 --- a/tests/group/group_result_samples/add_group_result_error.json +++ b/tests/group/group_result_samples/add_group_result_error.json @@ -9,9 +9,11 @@ "safReturnCode": 10, "returnCode": 2000, "reasonCode": 68, - "image": "", + "textInError": "name", + "errorOffset": 149, "messages": [ - "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + "Invalid attribute value specified.", + "Please note that for any text in error, redacted values may skew offset calculations." ] } ] diff --git a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json index 429bb8a2..47e4faa1 100644 --- a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json +++ b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json @@ -9,9 +9,11 @@ "safReturnCode": 10, "returnCode": 2000, "reasonCode": 68, - "image": "", + "textInError": "name", + "errorOffset": 149, "messages": [ - "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + "Invalid attribute value specified.", + "Please note that for any text in error, redacted values may skew offset calculations." ] } ] diff --git a/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log b/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log index 16f7cf33..fa796fe7 100644 --- a/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log +++ b/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log @@ -116,7 +116,7 @@ }, "returnCode": 0, "reasonCode": 0, - "runningUserid": "SQUIDWRD" + "runningUserid": "squidwrd" } } @@ -165,7 +165,7 @@ }, "returnCode": 0, "reasonCode": 0, - "runningUserid": "SQUIDWRD" + "runningUserid": "squidwrd" } } diff --git a/tests/user/user_log_samples/alter_user_success_as_eswift.log b/tests/user/user_log_samples/alter_user_success_as_eswift.log index c414e4f8..31effcf7 100644 --- a/tests/user/user_log_samples/alter_user_success_as_eswift.log +++ b/tests/user/user_log_samples/alter_user_success_as_eswift.log @@ -99,7 +99,7 @@ }, "returnCode": 0, "reasonCode": 0, - "runningUserid": "ESWIFT" + "runningUserid": "eswift" } } @@ -163,7 +163,7 @@ }, "returnCode": 0, "reasonCode": 0, - "runningUserid": "ESWIFT" + "runningUserid": "eswift" } } @@ -257,7 +257,7 @@ }, "returnCode": 0, "reasonCode": 0, - "runningUserid": "ESWIFT" + "runningUserid": "eswift" } } diff --git a/tests/user/user_result_samples/add_user_result_error.json b/tests/user/user_result_samples/add_user_result_error.json index 83223ee8..856455d3 100644 --- a/tests/user/user_result_samples/add_user_result_error.json +++ b/tests/user/user_result_samples/add_user_result_error.json @@ -9,9 +9,11 @@ "safReturnCode": 10, "returnCode": 2000, "reasonCode": 68, - "image": "", + "textInError": "name", + "errorOffset": 149, "messages": [ - "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + "Invalid attribute value specified.", + "Please note that for any text in error, redacted values may skew offset calculations." ] } ] diff --git a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json index 4a2f4e7a..233707c1 100644 --- a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json +++ b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json @@ -9,9 +9,11 @@ "safReturnCode": 10, "returnCode": 2000, "reasonCode": 68, - "image": "", + "textInError": "name", + "errorOffset": 149, "messages": [ - "Invalid attribute value specified.\nText in Error: 'name'\nApproximate Offset of Text in Error: '149'\nPlease note that for any text in error, redacted values may skew offset calculations." + "Invalid attribute value specified.", + "Please note that for any text in error, redacted values may skew offset calculations." ] } ] From 3c594f5f3a3489a225e918c87c4843347ec43c11 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Thu, 30 Nov 2023 16:08:44 -0500 Subject: [PATCH 12/30] Rearrange Errors Refactor IRRSMO00 Security Request Errors under Null Response Error Change Security Request Error to Security Response Error Change Null Response Error to Security Request Error Signed-off-by: Elijah Swift --- pyracf/__init__.py | 2 +- pyracf/common/null_response_error.py | 52 ------------- pyracf/common/security_admin.py | 30 ++++++-- pyracf/common/security_request_error.py | 73 +++++++++++++------ pyracf/common/security_response_error.py | 34 +++++++++ pyracf/common/security_result.py | 36 --------- pyracf/data_set/data_set_admin.py | 6 +- pyracf/group/group_admin.py | 6 +- pyracf/resource/resource_admin.py | 6 +- pyracf/scripts/install_precheck.py | 4 +- pyracf/user/user_admin.py | 6 +- tests/access/test_access_debug_logging.py | 4 +- tests/access/test_access_result_parser.py | 6 +- tests/common/test_common_constants.py | 6 +- ...rror.py => test_security_request_error.py} | 16 ++-- .../test_connection_debug_logging.py | 4 +- .../test_connection_result_parser.py | 6 +- tests/data_set/test_data_set_debug_logging.py | 6 +- tests/data_set/test_data_set_getters.py | 6 +- tests/data_set/test_data_set_result_parser.py | 10 +-- .../add_group_result_error.json | 21 ++---- ...ract_group_result_bad_attribute_error.json | 21 ++---- tests/group/test_group_debug_logging.py | 6 +- tests/group/test_group_getters.py | 14 ++-- tests/group/test_group_result_parser.py | 7 +- tests/resource/test_resource_debug_logging.py | 6 +- tests/resource/test_resource_getters.py | 6 +- tests/resource/test_resource_result_parser.py | 10 +-- .../test_resource_subfunction_extracts.py | 14 ++-- tests/setropts/test_setropts_debug_logging.py | 4 +- tests/setropts/test_setropts_getters.py | 6 +- tests/setropts/test_setropts_result_parser.py | 6 +- tests/test_runner.py | 4 +- tests/user/test_user_debug_logging.py | 16 ++-- tests/user/test_user_getters.py | 52 ++++++------- tests/user/test_user_result_parser.py | 17 +++-- .../add_user_result_error.json | 21 ++---- ...tract_user_result_bad_attribute_error.json | 21 ++---- 38 files changed, 274 insertions(+), 297 deletions(-) delete mode 100644 pyracf/common/null_response_error.py create mode 100644 pyracf/common/security_response_error.py rename tests/common/{test_null_response_error.py => test_security_request_error.py} (73%) diff --git a/pyracf/__init__.py b/pyracf/__init__.py index 13faad7b..97a9357a 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,8 +2,8 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError -from .common.null_response_error import NullResponseError from .common.security_request_error import SecurityRequestError +from .common.security_response_error import SecurityResponseError from .common.segment_error import SegmentError from .common.segment_trait_error import SegmentTraitError from .common.userid_error import UserIdError diff --git a/pyracf/common/null_response_error.py b/pyracf/common/null_response_error.py deleted file mode 100644 index 37aeb7a1..00000000 --- a/pyracf/common/null_response_error.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Exception to use when no data is returned by IRRSMO00.""" -from typing import Union - - -class NullResponseError(Exception): - """ - Raised when no xml string is returned by IRRSMO00. - """ - - def __init__( - self, xml_str: str, request_xml: bytes, run_as_userid: Union[str, None] = None - ) -> None: - self.message = "Security request made to IRRSMO00 failed." - self.saf_return_code = xml_str[0] - self.racf_return_code = xml_str[1] - self.racf_reason_code = xml_str[2] - self.request_xml = request_xml.decode("utf-8") - self.message += ( - f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" - + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" - ) - if ( - (self.saf_return_code == 8) - and (self.racf_return_code == 200) - and (self.racf_reason_code == 16) - ): - self.message += ( - "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." - ) - elif ( - (self.saf_return_code == 8) - and (self.racf_return_code == 200) - and (self.racf_reason_code == 8) - ): - self.message += ( - "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE" - + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." - ) - else: - self.message += ( - "\n\nPlease check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" - + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" - ) - self.message = f"({self.__class__.__name__}) {self.message}" - - def __str__(self) -> str: - return self.message diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index e5c9b096..35a838c4 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -7,9 +7,9 @@ from .irrsmo00 import IRRSMO00 from .logger import Logger -from .null_response_error import NullResponseError from .security_request import SecurityRequest from .security_request_error import SecurityRequestError +from .security_response_error import SecurityResponseError from .security_result import SecurityResult from .segment_error import SegmentError from .segment_trait_error import SegmentTraitError @@ -203,8 +203,10 @@ def _make_request( if isinstance(result_xml, list): # When IRRSMO00 encounters some errors, it returns no XML response string. # When this happens, the c code instead surfaces the return and reason - # codes which causes a NullResponseError to be raised. - raise NullResponseError(result_xml, request_xml, self.get_running_userid()) + # codes which causes a SecurityRequestError to be raised. + raise SecurityRequestError( + result_xml, request_xml, self.get_running_userid() + ) if self.__debug: # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. @@ -217,13 +219,29 @@ def _make_request( "Result Dictionary", results.get_result_dictionary() ) result_dictionary = results.get_result_dictionary() + if result_dictionary["securityResult"]["returnCode"] > 4: + # All return codes greater than 4 are indicative of an issue with + # IRRSMO00 that would stop a command image from being generated. + # The user should interogate the result dictionary attached to the + # SecurityResponseError as well as the return and reason codes to + # resolve the problem. + raise SecurityRequestError( + [ + 8, + result_dictionary["securityResult"]["returnCode"], + result_dictionary["securityResult"]["reasonCode"], + ], + request_xml, + self.get_running_userid(), + result_dictionary, + ) if result_dictionary["securityResult"]["returnCode"] != 0: - # All non-zero return codes should cause a SecurityRequestError to be raised. + # All non-zero return codes should cause a SecurityResponseError to be raised. # Even if a return code of 4 is not indicative of a problem, it it is # up to the user to interogate the result dictionary attached to the - # SecurityRequestError and decided whether or not the return code 4 is + # SecurityResponseError and decided whether or not the return code 4 is # indicative of a problem. - raise SecurityRequestError(result_dictionary, request_xml) + raise SecurityResponseError(result_dictionary) return result_dictionary def _to_steps(self, results: Union[List[dict], dict, bytes]) -> Union[dict, bytes]: diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 947a176c..656fa6b8 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -1,35 +1,66 @@ -"""Exception to use when data returned by IRRSMO00 indicates that the request failed.""" +"""Exception to use when no data is returned by IRRSMO00.""" +from typing import Union class SecurityRequestError(Exception): """ - Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. + Raised when no xml string is returned by IRRSMO00. """ - def __init__(self, result: dict, request_xml: bytes) -> None: + def __init__( + self, + xml_str: str, + request_xml: bytes, + run_as_userid: Union[str, None] = None, + result_dict: dict = None, + ) -> None: self.message = "Security request made to IRRSMO00 failed." - self.result = result + self.saf_return_code = xml_str[0] + self.racf_return_code = xml_str[1] + self.racf_reason_code = xml_str[2] self.request_xml = request_xml.decode("utf-8") self.message += ( - "\n\nSee results dictionary " - + f"'{self.__class__.__name__}.result' for more details." + f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" + + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" ) + if result_dict is not None: + self.message += ( + "\n\nSee results dictionary " + + f"'{self.__class__.__name__}.result' for more details.\n" + + "\n\nYou can also check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" + ) + self.result = result_dict + elif ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 16) + ): + self.message += ( + "\n\nCheck to see if the proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + ) + elif ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 8) + ): + self.message += ( + "\n\nCheck to see if the proper RACF permissions are in place.\n" + + "For the `run_as_userid` feature, you must have at least UPDATE" + + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." + ) + else: + self.message += ( + "\n\nPlease check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" + ) self.message = f"({self.__class__.__name__}) {self.message}" def __str__(self) -> str: return self.message - - def contains_error_message( - self, security_definition_tag: str, error_message_id: str - ): - """Checks to see if specific error message id appears in the security request error""" - commands = self.result["securityResult"][security_definition_tag].get( - "commands" - ) - if not isinstance(commands, list): - return False - messages = commands[0].get("messages", []) - if error_message_id in "".join(messages): - return True - else: - return False diff --git a/pyracf/common/security_response_error.py b/pyracf/common/security_response_error.py new file mode 100644 index 00000000..f9701438 --- /dev/null +++ b/pyracf/common/security_response_error.py @@ -0,0 +1,34 @@ +"""Exception to use when data returned by IRRSMO00 indicates that the request failed.""" + + +class SecurityResponseError(Exception): + """ + Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. + """ + + def __init__(self, result: dict) -> None: + self.message = "Security request made to IRRSMO00 failed." + self.result = result + self.message += ( + "\n\nSee results dictionary " + + f"'{self.__class__.__name__}.result' for more details." + ) + self.message = f"({self.__class__.__name__}) {self.message}" + + def __str__(self) -> str: + return self.message + + def contains_error_message( + self, security_definition_tag: str, error_message_id: str + ): + """Checks to see if specific error message id appears in the security request error""" + commands = self.result["securityResult"][security_definition_tag].get( + "commands" + ) + if not isinstance(commands, list): + return False + messages = commands[0].get("messages", []) + if error_message_id in "".join(messages): + return True + else: + return False diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index 78aee2ff..f460c1cd 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -16,7 +16,6 @@ def __init__( self.__result = XMLParser.fromstring(result_xml) self.__result_dictionary = {"securityResult": {}} self.__extract_results() - self.__result_dictionary = self.__check_result_type(self.__result_dictionary) if running_userid is not None: self.__result_dictionary["securityResult"][ "runningUserid" @@ -64,15 +63,6 @@ def __extract_info(self) -> None: info.append(item.text) self.__definition.remove(item) - def __check_result_type(self, result: dict) -> dict: - not_profiles = ["returnCode", "reasonCode", "runningUserid"] - for profile_type in result["securityResult"]: - if profile_type in not_profiles: - continue - if "error" in result["securityResult"][profile_type]: - return self.__organize_smo_error(result, profile_type) - return result - def __extract_commands(self, filter_out_extra_messages: bool) -> None: """Extract commands section from XML into a list.""" self.__definition_dictionary["commands"] = [] @@ -117,32 +107,6 @@ def __extract_error(self) -> None: except ValueError: self.__definition_dictionary["error"][item_tag] = item.text - def __organize_smo_error(self, result: dict, profile_type: str) -> dict: - command_result = {} - command_result["safReturnCode"] = result["securityResult"][profile_type][ - "error" - ]["errorFunction"] - command_result["returnCode"] = result["securityResult"][profile_type]["error"][ - "errorCode" - ] - command_result["reasonCode"] = result["securityResult"][profile_type]["error"][ - "errorReason" - ] - command_result["errorOffset"] = result["securityResult"][profile_type]["error"][ - "errorOffset" - ] - command_result["textInError"] = result["securityResult"][profile_type]["error"][ - "textInError" - ] - command_result["messages"] = [ - result["securityResult"][profile_type]["error"]["errorMessage"], - "Please note that for any text in error," - + " redacted values may skew offset calculations.", - ] - result["securityResult"][profile_type]["commands"] = [command_result] - del result["securityResult"][profile_type]["error"] - return result - def __to_pascal_case(self, key: str) -> str: """Convert result dictionary keys to pascal case.""" match (key): diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index 2b1df499..83bbe02b 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_request_error import SecurityRequestError +from pyracf.common.security_response_error import SecurityResponseError from .data_set_request import DataSetRequest @@ -117,7 +117,7 @@ def add( ) if self._get_field(profile, "base", "name") == data_set.lower(): raise AddOperationError(data_set, self._profile_type) - except SecurityRequestError as exception: + except SecurityResponseError as exception: if not exception.contains_error_message(self._profile_type, "ICH35003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -142,7 +142,7 @@ def alter( profile = self.extract( data_set, volume=volume, generic=generic, profile_only=True ) - except SecurityRequestError: + except SecurityResponseError: raise AlterOperationError(data_set, self._profile_type) if not self._get_field(profile, "base", "name") == data_set.lower(): raise AlterOperationError(data_set, self._profile_type) diff --git a/pyracf/group/group_admin.py b/pyracf/group/group_admin.py index bd78851d..4c057eef 100644 --- a/pyracf/group/group_admin.py +++ b/pyracf/group/group_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_request_error import SecurityRequestError +from pyracf.common.security_response_error import SecurityResponseError from .group_request import GroupRequest @@ -131,7 +131,7 @@ def add(self, group: str, traits: dict = {}) -> Union[dict, bytes]: return self._make_request(group_request) try: self.extract(group) - except SecurityRequestError as exception: + except SecurityResponseError as exception: if not exception.contains_error_message(self._profile_type, "ICH51003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -149,7 +149,7 @@ def alter(self, group: str, traits: dict) -> Union[dict, bytes]: return self._make_request(group_request, irrsmo00_precheck=True) try: self.extract(group) - except SecurityRequestError: + except SecurityResponseError: raise AlterOperationError(group, self._profile_type) self._build_segment_trait_dictionary(traits) group_request = GroupRequest(group, "set") diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index 87d6cd64..d749a8d5 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_request_error import SecurityRequestError +from pyracf.common.security_response_error import SecurityResponseError from .resource_request import ResourceRequest @@ -510,7 +510,7 @@ def add( profile = self.extract(resource, class_name, profile_only=True) if self._get_field(profile, "base", "name") == resource.lower(): raise AddOperationError(resource, class_name) - except SecurityRequestError as exception: + except SecurityResponseError as exception: if not exception.contains_error_message(self._profile_type, "ICH13003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -527,7 +527,7 @@ def alter(self, resource: str, class_name: str, traits: dict) -> Union[dict, byt return self._make_request(profile_request, irrsmo00_precheck=True) try: profile = self.extract(resource, class_name, profile_only=True) - except SecurityRequestError: + except SecurityResponseError: raise AlterOperationError(resource, class_name) if not self._get_field(profile, "base", "name") == resource.lower(): raise AlterOperationError(resource, class_name) diff --git a/pyracf/scripts/install_precheck.py b/pyracf/scripts/install_precheck.py index c1fb2154..276a2937 100644 --- a/pyracf/scripts/install_precheck.py +++ b/pyracf/scripts/install_precheck.py @@ -1,11 +1,11 @@ -from pyracf import ResourceAdmin, SecurityRequestError +from pyracf import ResourceAdmin, SecurityResponseError def define_precheck_profile(): resource_admin = ResourceAdmin() try: access = resource_admin.get_my_access("IRR.IRRSMO00.PRECHECK", "XFACILIT") - except SecurityRequestError: + except SecurityResponseError: traits_precheck = {"base:universal_access": "None"} result = resource_admin.add( "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck diff --git a/pyracf/user/user_admin.py b/pyracf/user/user_admin.py index 6d1deb9a..44caf1b8 100644 --- a/pyracf/user/user_admin.py +++ b/pyracf/user/user_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_request_error import SecurityRequestError +from pyracf.common.security_response_error import SecurityResponseError from .user_request import UserRequest @@ -777,7 +777,7 @@ def add(self, userid: str, traits: dict = {}) -> Union[dict, bytes]: return self._make_request(user_request) try: self.extract(userid) - except SecurityRequestError as exception: + except SecurityResponseError as exception: if not exception.contains_error_message(self._profile_type, "ICH30001I"): raise exception self._build_segment_trait_dictionary(traits) @@ -795,7 +795,7 @@ def alter(self, userid: str, traits: dict) -> Union[dict, bytes]: return self._make_request(user_request, irrsmo00_precheck=True) try: self.extract(userid) - except SecurityRequestError: + except SecurityResponseError: raise AlterOperationError(userid, self._profile_type) self._build_segment_trait_dictionary(traits) user_request = UserRequest(userid, "set") diff --git a/tests/access/test_access_debug_logging.py b/tests/access/test_access_debug_logging.py index 8f70e86b..6cb6d682 100644 --- a/tests/access/test_access_debug_logging.py +++ b/tests/access/test_access_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.access.test_access_constants as TestAccessConstants -from pyracf import AccessAdmin, SecurityRequestError +from pyracf import AccessAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -56,7 +56,7 @@ def test_permit_access_request_debug_log_works_on_error( self.access_admin.permit( "TESTING", "ELIJTEST", "MCGINLEY", traits={"base:access": "ALTER"} ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestAccessConstants.TEST_PERMIT_ACCESS_ERROR_LOG) diff --git a/tests/access/test_access_result_parser.py b/tests/access/test_access_result_parser.py index 3adb4e9c..950a56e0 100644 --- a/tests/access/test_access_result_parser.py +++ b/tests/access/test_access_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.access.test_access_constants as TestAccessConstants -from pyracf import AccessAdmin, SecurityRequestError +from pyracf import AccessAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -44,7 +44,7 @@ def test_access_admin_can_parse_permit_access_error_xml( call_racf_mock.return_value = ( TestAccessConstants.TEST_PERMIT_ACCESS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.access_admin.permit( "TESTING", "ELIJTEST", "MCGINLEY", traits={"base:access": "ALTER"} ) @@ -76,7 +76,7 @@ def test_access_admin_can_parse_delete_access_error_xml( call_racf_mock.return_value = ( TestAccessConstants.TEST_DELETE_ACCESS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.access_admin.delete("TESTING", "ELIJTEST", "ESWIFT") self.assertEqual( exception.exception.result, diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index c46638d4..981a6831 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -118,7 +118,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: # ============================================================================ TEST_NULL_RESPONSE_PRECHECK_TEXT = ( - "(NullResponseError) Security request made to IRRSMO00 failed." + "(SecurityRequestError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " @@ -126,7 +126,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) TEST_NULL_RESPONSE_SURROGAT_TEXT = ( - "(NullResponseError) Security request made to IRRSMO00 failed." + "(SecurityRequestError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" @@ -134,7 +134,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) TEST_NULL_RESPONSE_GENERIC_TEXT = ( - "(NullResponseError) Security request made to IRRSMO00 failed." + "(SecurityRequestError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" diff --git a/tests/common/test_null_response_error.py b/tests/common/test_security_request_error.py similarity index 73% rename from tests/common/test_null_response_error.py rename to tests/common/test_security_request_error.py index dea1e016..64ec1bd1 100644 --- a/tests/common/test_null_response_error.py +++ b/tests/common/test_security_request_error.py @@ -6,7 +6,7 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import NullResponseError, UserAdmin +from pyracf import SecurityRequestError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -14,42 +14,42 @@ @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") -class TestNullResponseError(unittest.TestCase): +class TestSecurityRequestError(unittest.TestCase): maxDiff = None IRRSMO00.__init__ = Mock(return_value=None) user_admin = UserAdmin() - def test_null_response_error_thrown_on_precheck_error( + def test_security_request_error_thrown_on_precheck_error( self, call_racf_mock: Mock, ): call_racf_mock.return_value = [8, 200, 16] - with self.assertRaises(NullResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.set_password("TESTUSER", "Testpass") self.assertEqual( exception.exception.message, TestCommonConstants.TEST_NULL_RESPONSE_PRECHECK_TEXT, ) - def test_null_response_error_thrown_on_surrogat_error( + def test_security_request_error_thrown_on_surrogat_error( self, call_racf_mock: Mock, ): call_racf_mock.return_value = [8, 200, 8] self.user_admin.set_running_userid("ESWIFT") - with self.assertRaises(NullResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, TestCommonConstants.TEST_NULL_RESPONSE_SURROGAT_TEXT, ) - def test_null_response_error_thrown_on_miscellaneous_error( + def test_security_request_error_thrown_on_miscellaneous_error( self, call_racf_mock: Mock, ): call_racf_mock.return_value = [8, 2000, 20] - with self.assertRaises(NullResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, diff --git a/tests/connection/test_connection_debug_logging.py b/tests/connection/test_connection_debug_logging.py index 7adabd20..fd0fbec5 100644 --- a/tests/connection/test_connection_debug_logging.py +++ b/tests/connection/test_connection_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.connection.test_connection_constants as TestConnectionConstants -from pyracf import ConnectionAdmin, SecurityRequestError +from pyracf import ConnectionAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_connect_connection_request_debug_log_works_on_error( "TESTGRP0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ), - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/connection/test_connection_result_parser.py b/tests/connection/test_connection_result_parser.py index dcd724a2..b84752c5 100644 --- a/tests/connection/test_connection_result_parser.py +++ b/tests/connection/test_connection_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.connection.test_connection_constants as TestConnectionConstants -from pyracf import ConnectionAdmin, SecurityRequestError +from pyracf import ConnectionAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -46,7 +46,7 @@ def test_connection_admin_can_parse_connect_connection_error_xml( call_racf_mock.return_value = ( TestConnectionConstants.TEST_CONNECT_CONNECTION_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.connection_admin.connect( "ESWIFT", "TESTGRP0", @@ -80,7 +80,7 @@ def test_connection_admin_can_parse_delete_connection_error_xml( call_racf_mock.return_value = ( TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.connection_admin.delete("ESWIFT", "TESTGRP0") self.assertEqual( exception.exception.result, diff --git a/tests/data_set/test_data_set_debug_logging.py b/tests/data_set/test_data_set_debug_logging.py index 01a13452..def94a00 100644 --- a/tests/data_set/test_data_set_debug_logging.py +++ b/tests/data_set/test_data_set_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.data_set.test_data_set_constants as TestDataSetConstants -from pyracf import DataSetAdmin, SecurityRequestError +from pyracf import DataSetAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_alter_data_set_request_debug_log_works_on_error( "ESWIFT.TEST.T1136242.P3020470", traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestDataSetConstants.TEST_ALTER_DATA_SET_ERROR_LOG) @@ -95,7 +95,7 @@ def test_extract_data_set_base_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/data_set/test_data_set_getters.py b/tests/data_set/test_data_set_getters.py index 0437b283..494bb0bf 100644 --- a/tests/data_set/test_data_set_getters.py +++ b/tests/data_set/test_data_set_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.data_set.test_data_set_constants as TestDataSetConstants -from pyracf import DataSetAdmin, SecurityRequestError +from pyracf import DataSetAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_data_set_admin_get_universal_access_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.data_set_admin.get_universal_access("ESWIFT.TEST.T1136242.P3020470") def test_data_set_admin_get_my_access_returns_valid_when_alter( @@ -98,5 +98,5 @@ def test_data_set_admin_get_my_access_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.data_set_admin.get_my_access("ESWIFT.TEST.T1136242.P3020470") diff --git a/tests/data_set/test_data_set_result_parser.py b/tests/data_set/test_data_set_result_parser.py index c4417d35..653cd449 100644 --- a/tests/data_set/test_data_set_result_parser.py +++ b/tests/data_set/test_data_set_result_parser.py @@ -10,7 +10,7 @@ AddOperationError, AlterOperationError, DataSetAdmin, - SecurityRequestError, + SecurityResponseError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -90,7 +90,7 @@ def test_data_set_admin_can_parse_add_data_set_error_xml( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BAD_ATTRIBUTE_ERROR_XML, TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.data_set_admin.add( "ESWIFTTESTT1136242P3020470", traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, @@ -172,7 +172,7 @@ def test_data_set_admin_can_parse_alter_data_set_error_xml( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.data_set_admin.alter( "ESWIFT.TEST.T1136242.P3020470", traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, @@ -217,7 +217,7 @@ def test_data_set_admin_can_parse_extract_data_set_base_only_error_xml( call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") self.assertEqual( exception.exception.result, @@ -247,7 +247,7 @@ def test_data_set_admin_can_parse_delete_data_set_error_xml( call_racf_mock.return_value = ( TestDataSetConstants.TEST_DELETE_DATA_SET_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.data_set_admin.delete("ESWIFT.TEST.T1136242.P3020470") self.assertEqual( exception.exception.result, diff --git a/tests/group/group_result_samples/add_group_result_error.json b/tests/group/group_result_samples/add_group_result_error.json index 24a0296c..cf98e80f 100644 --- a/tests/group/group_result_samples/add_group_result_error.json +++ b/tests/group/group_result_samples/add_group_result_error.json @@ -4,19 +4,14 @@ "name": "TESTGRPP0", "operation": "set", "requestId": "GroupRequest", - "commands": [ - { - "safReturnCode": 10, - "returnCode": 2000, - "reasonCode": 68, - "textInError": "name", - "errorOffset": 149, - "messages": [ - "Invalid attribute value specified.", - "Please note that for any text in error, redacted values may skew offset calculations." - ] - } - ] + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "textInError": "name", + "errorOffset": 149, + "errorMessage": "Invalid attribute value specified." + } }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json index 47e4faa1..5dfa55b7 100644 --- a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json +++ b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json @@ -4,19 +4,14 @@ "name": "TESTGRPP0", "operation": "listdata", "requestId": "GroupRequest", - "commands": [ - { - "safReturnCode": 10, - "returnCode": 2000, - "reasonCode": 68, - "textInError": "name", - "errorOffset": 149, - "messages": [ - "Invalid attribute value specified.", - "Please note that for any text in error, redacted values may skew offset calculations." - ] - } - ] + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "textInError": "name", + "errorOffset": 149, + "errorMessage": "Invalid attribute value specified." + } }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/group/test_group_debug_logging.py b/tests/group/test_group_debug_logging.py index d4d63265..0d33c8d1 100644 --- a/tests/group/test_group_debug_logging.py +++ b/tests/group/test_group_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.group.test_group_constants as TestGroupConstants -from pyracf import GroupAdmin, SecurityRequestError +from pyracf import GroupAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -57,7 +57,7 @@ def test_alter_group_request_debug_log_works_on_error( "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestGroupConstants.TEST_ALTER_GROUP_ERROR_LOG) @@ -91,7 +91,7 @@ def test_extract_group_base_omvs_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.group_admin.extract("TESTGRP0", segments=["omvs"]) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/group/test_group_getters.py b/tests/group/test_group_getters.py index 25177201..9805fa2a 100644 --- a/tests/group/test_group_getters.py +++ b/tests/group/test_group_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.group.test_group_constants as TestGroupConstants -from pyracf import GroupAdmin, SecurityRequestError +from pyracf import GroupAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -52,7 +52,7 @@ def test_group_admin_has_group_special_authority_raises_an_exception_when_extrac call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.has_group_special_authority("TESTGRP0", "ESWIFT") # ============================================================================ @@ -88,7 +88,7 @@ def test_group_admin_has_group_operations_authority_raises_an_exception_when_ext call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.has_group_operations_authority("TESTGRP0", "LEONARD") # ============================================================================ @@ -129,7 +129,7 @@ def test_group_admin_has_group_auditor_authority_raises_an_exception_when_extrac call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.has_group_auditor_authority("TESTGRP0", "ESWIFT") # ============================================================================ @@ -170,7 +170,7 @@ def test_group_admin_has_group_access_attribute_raises_an_exception_when_extract call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.has_group_access_attribute("TESTGRP0", "LEONARD") # ============================================================================ @@ -193,7 +193,7 @@ def test_group_admin_get_omvs_gid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.get_omvs_gid("TESTGRP0"), 1234567 def test_group_admin_get_omvs_gid_returns_none_when_no_omvs_segment_exists( @@ -229,7 +229,7 @@ def test_group_admin_get_ovm_gid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.group_admin.get_ovm_gid("TESTGRP0"), 1234567 def test_group_admin_get_ovm_gid_returns_none_when_no_ovm_segment_exists( diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index b02441da..772a9c59 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -11,6 +11,7 @@ AlterOperationError, GroupAdmin, SecurityRequestError, + SecurityResponseError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -127,7 +128,7 @@ def test_group_admin_can_parse_alter_group_error_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.group_admin.alter( "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, @@ -172,7 +173,7 @@ def test_group_admin_can_parse_extract_group_base_omvs_error_xml( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.group_admin.extract("TESTGRP0", segments=["omvs"]) self.assertEqual( exception.exception.result, @@ -202,7 +203,7 @@ def test_group_admin_can_parse_delete_group_error_xml( call_racf_mock.return_value = ( TestGroupConstants.TEST_DELETE_GROUP_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.group_admin.delete("TESTGRP0") self.assertEqual( exception.exception.result, diff --git a/tests/resource/test_resource_debug_logging.py b/tests/resource/test_resource_debug_logging.py index 8342649d..9a83ad3f 100644 --- a/tests/resource/test_resource_debug_logging.py +++ b/tests/resource/test_resource_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityRequestError +from pyracf import ResourceAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -62,7 +62,7 @@ def test_alter_resource_request_debug_log_works_on_error( "ELIJTEST", traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestResourceConstants.TEST_ALTER_RESOURCE_ERROR_LOG) @@ -96,7 +96,7 @@ def test_extract_resource_base_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.resource_admin.extract("TESTING", "ELIJTEST") - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/resource/test_resource_getters.py b/tests/resource/test_resource_getters.py index 4f321ed1..b6ffc315 100644 --- a/tests/resource/test_resource_getters.py +++ b/tests/resource/test_resource_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityRequestError +from pyracf import ResourceAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -57,7 +57,7 @@ def test_resource_admin_get_universal_access_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.get_universal_access("TESTING", "ELIJTEST") def test_resource_admin_get_my_access_returns_valid_when_read( @@ -93,5 +93,5 @@ def test_resource_admin_get_my_access_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.get_my_access("TESTING", "ELIJTEST") diff --git a/tests/resource/test_resource_result_parser.py b/tests/resource/test_resource_result_parser.py index 525366ee..e59f1377 100644 --- a/tests/resource/test_resource_result_parser.py +++ b/tests/resource/test_resource_result_parser.py @@ -10,7 +10,7 @@ AddOperationError, AlterOperationError, ResourceAdmin, - SecurityRequestError, + SecurityResponseError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -85,7 +85,7 @@ def test_resource_admin_can_parse_add_resource_error_xml( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BAD_CLASS_ERROR_XML, TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.resource_admin.add("TESTING", "ELIXTEST") self.assertEqual( exception.exception.result, @@ -167,7 +167,7 @@ def test_resource_admin_can_parse_alter_resource_error_xml( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.resource_admin.alter( "TESTING", "ELIJTEST", @@ -214,7 +214,7 @@ def test_resource_admin_can_parse_extract_resource_base_error_xml( call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.resource_admin.extract("TESTING", "ELIJTEST") self.assertEqual( exception.exception.result, @@ -244,7 +244,7 @@ def test_resource_admin_can_parse_delete_resource_error_xml( call_racf_mock.return_value = ( TestResourceConstants.TEST_DELETE_RESOURCE_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.resource_admin.delete("TESTING", "ELIJTEST") self.assertEqual( exception.exception.result, diff --git a/tests/resource/test_resource_subfunction_extracts.py b/tests/resource/test_resource_subfunction_extracts.py index 2f4787aa..b7eac0f0 100644 --- a/tests/resource/test_resource_subfunction_extracts.py +++ b/tests/resource/test_resource_subfunction_extracts.py @@ -6,7 +6,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityRequestError +from pyracf import ResourceAdmin, SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -41,7 +41,7 @@ def test_resource_admin_extract_resource_class_raises_exception_when_extract_fai call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_CDTINFO_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_resource_class("SHELCITY") # ============================================================================ @@ -66,7 +66,7 @@ def test_resource_admin_build_extract_started_task_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_STDATA_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_started_task("TSTTSKEL") # ============================================================================ @@ -91,7 +91,7 @@ def test_resource_admin_build_extract_custom_field_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_CFDEF_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_custom_field("TVSHOW", "user") # ============================================================================ @@ -116,7 +116,7 @@ def test_resource_admin_build_extract_kerberos_realm_raises_exception_when_extra call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_KERB_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_kerberos_realm("TSTREALM") # ============================================================================ @@ -141,7 +141,7 @@ def test_resource_admin_build_extract_signed_program_raises_exception_when_extra call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SIGVER_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_signed_program("TESTPRGM") # ============================================================================ @@ -166,5 +166,5 @@ def test_resource_admin_build_extract_appc_session_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SESSION_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.resource_admin.extract_appc_session("TSTNET", "TSTLOCLU", "TSTPRTLU") diff --git a/tests/setropts/test_setropts_debug_logging.py b/tests/setropts/test_setropts_debug_logging.py index b6876dd2..2af953e2 100644 --- a/tests/setropts/test_setropts_debug_logging.py +++ b/tests/setropts/test_setropts_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityRequestError, SetroptsAdmin +from pyracf import SecurityResponseError, SetroptsAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -52,7 +52,7 @@ def test_alter_setropts_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.setropts_admin.alter(options={"base:raclist": "ELIXTEST"}) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestSetroptsConstants.TEST_ALTER_SETROPTS_ERROR_LOG) diff --git a/tests/setropts/test_setropts_getters.py b/tests/setropts/test_setropts_getters.py index 2f52675d..7a39ca9b 100644 --- a/tests/setropts/test_setropts_getters.py +++ b/tests/setropts/test_setropts_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityRequestError +from pyracf import SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 from pyracf.setropts.setropts_admin import SetroptsAdmin @@ -43,7 +43,7 @@ def test_setropts_admin_get_password_rules_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.setropts_admin.get_password_rules() # ============================================================================ @@ -70,5 +70,5 @@ def test_setropts_admin_get_class_attributes_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.setropts_admin.get_class_attributes("FACILITY") diff --git a/tests/setropts/test_setropts_result_parser.py b/tests/setropts/test_setropts_result_parser.py index 3ebfe7bb..790d2c15 100644 --- a/tests/setropts/test_setropts_result_parser.py +++ b/tests/setropts/test_setropts_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityRequestError +from pyracf import SecurityResponseError from pyracf.common.irrsmo00 import IRRSMO00 from pyracf.setropts.setropts_admin import SetroptsAdmin @@ -43,7 +43,7 @@ def test_setropts_admin_can_parse_add_setropts_error_xml( call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.setropts_admin.alter(options={"base:raclist": "elixtest"}) self.assertEqual( exception.exception.result, @@ -97,7 +97,7 @@ def test_setropts_admin_can_parse_list_setropts_error_xml( call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: # Not valid error for this request, but good test self.setropts_admin.list_racf_options() self.assertEqual( diff --git a/tests/test_runner.py b/tests/test_runner.py index 1a5850f3..e46b18a2 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -11,8 +11,8 @@ from tests.common.test_customize_segment_traits import TestCustomizeSegmentTraits from tests.common.test_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger -from tests.common.test_null_response_error import TestNullResponseError from tests.common.test_run_as_userid import TestRunAsUserId +from tests.common.test_security_request_error import TestSecurityRequestError from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -66,7 +66,7 @@ def __test_suite() -> unittest.TestSuite: TestCustomizeSegmentTraits, TestInstallPrecheckScript, TestLogger, - TestNullResponseError, + TestSecurityRequestError, TestRunAsUserId, TestConnectionResultParser, TestConnectionRequestBuilder, diff --git a/tests/user/test_user_debug_logging.py b/tests/user/test_user_debug_logging.py index c3b27d08..3a3e157e 100644 --- a/tests/user/test_user_debug_logging.py +++ b/tests/user/test_user_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.user.test_user_constants as TestUserConstants -from pyracf import SecurityRequestError, UserAdmin +from pyracf import SecurityResponseError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -61,7 +61,7 @@ def test_alter_user_request_debug_log_works_on_error( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestUserConstants.TEST_ALTER_USER_ERROR_LOG) @@ -108,7 +108,7 @@ def test_alter_user_request_debug_log_passwords_get_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -155,7 +155,7 @@ def test_alter_user_request_debug_log_passphrases_get_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -204,7 +204,7 @@ def test_alter_user_request_debug_log_passphrases_and_passwords_get_redacted_on_ "squidwrd", traits=error_traits, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -254,7 +254,7 @@ def test_alter_user_request_debug_log_password_xml_tags_not_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -309,7 +309,7 @@ def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_er "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, ) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -350,7 +350,7 @@ def test_extract_user_base_omvs_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.user_admin.extract("squidwrd", segments=["omvs"]) - except SecurityRequestError: + except SecurityResponseError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/user/test_user_getters.py b/tests/user/test_user_getters.py index f184cb11..a4dc4086 100644 --- a/tests/user/test_user_getters.py +++ b/tests/user/test_user_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.user.test_user_constants as TestUserConstants -from pyracf import SecurityRequestError, UserAdmin +from pyracf import SecurityResponseError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -53,7 +53,7 @@ def test_user_admin_has_special_authority_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.has_special_authority("squidwrd") # ============================================================================ @@ -91,7 +91,7 @@ def test_user_admin_has_auditory_authority_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.has_auditor_authority("squidwrd") # ============================================================================ @@ -128,7 +128,7 @@ def test_user_admin_has_operations_authority_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.has_operations_authority("squidwrd") # ============================================================================ @@ -162,7 +162,7 @@ def test_user_admin_get_class_authorizations_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_class_authorizations("squidwrd") # ============================================================================ @@ -268,7 +268,7 @@ def test_user_admin_get_omvs_uid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_uid("squidwrd") def test_user_admin_get_omvs_uid_returns_none_when_no_omvs_segment_exists( @@ -301,7 +301,7 @@ def test_user_admin_get_omvs_max_address_space_size_raises_an_exception_when_ext call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_address_space_size("squidwrd") def test_user_admin_get_omvs_max_address_space_size_returns_none_when_no_omvs_segment_exists( @@ -332,7 +332,7 @@ def test_user_admin_get_omvs_max_cpu_time_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_cpu_time("squidwrd") def test_user_admin_get_omvs_max_cpu_time_returns_none_when_no_omvs_segment_exists( @@ -363,7 +363,7 @@ def test_user_admin_get_omvs_max_files_per_process_raises_an_exception_when_extr call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_files_per_process("squidwrd") def test_user_admin_get_omvs_max_files_per_process_returns_none_when_no_omvs_segment_exists( @@ -396,7 +396,7 @@ def test_user_admin_get_omvs_max_non_shared_memory_raises_an_exception_when_extr call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_non_shared_memory("squidwrd") def test_user_admin_get_omvs_max_non_shared_memory_returns_none_when_field_is_unset( @@ -438,7 +438,7 @@ def test_user_admin_get_omvs_max_file_mapping_pages_raises_an_exception_when_ext call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_file_mapping_pages("squidwrd") def test_user_admin_get_omvs_max_file_mapping_pages_returns_none_when_no_omvs_segment_exists( @@ -469,7 +469,7 @@ def test_user_admin_get_omvs_max_processes_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_processes("squidwrd") def test_user_admin_get_omvs_max_processes_returns_none_when_no_omvs_segment_exists( @@ -500,7 +500,7 @@ def test_user_admin_get_omvs_max_shared_memory_raises_an_exception_when_extract_ call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_shared_memory("squidwrd") def test_user_admin_get_omvs_max_shared_memory_returns_none_when_field_is_unset( @@ -540,7 +540,7 @@ def test_user_admin_get_omvs_max_threads_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_max_threads("squidwrd") def test_user_admin_get_omvs_max_threads_returns_none_when_no_omvs_segment_exists( @@ -573,7 +573,7 @@ def test_user_admin_get_omvs_home_directory_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_home_directory("squidwrd") def test_user_admin_get_omvs_home_directory_returns_none_when_no_omvs_segment_exists( @@ -626,7 +626,7 @@ def test_user_admin_get_omvs_default_shell_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_omvs_default_shell("squidwrd") def test_user_admin_get_omvs_default_shell_returns_none_when_no_omvs_segment_exists( @@ -675,7 +675,7 @@ def test_user_admin_get_tso_account_number_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_account_number("squidwrd") def test_user_admin_get_tso_account_number_returns_none_when_no_tso_segment_exists( @@ -715,7 +715,7 @@ def test_user_admin_get_tso_logon_command_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_logon_command("squidwrd") def test_user_admin_get_tso_logon_command_returns_none_when_no_tso_segment_exists( @@ -755,7 +755,7 @@ def test_user_admin_get_tso_hold_class_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_hold_class("squidwrd") def test_user_admin_get_tso_hold_class_returns_none_when_no_tso_segment_exists( @@ -795,7 +795,7 @@ def test_user_admin_get_tso_max_region_size_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_max_region_size("squidwrd") def test_user_admin_get_tso_max_region_size_returns_none_when_no_tso_segment_exists( @@ -826,7 +826,7 @@ def test_user_admin_get_tso_message_class_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_message_class("squidwrd") def test_user_admin_get_tso_message_class_returns_none_when_no_tso_segment_exists( @@ -866,7 +866,7 @@ def test_user_admin_get_tso_logon_procedure_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_logon_procedure("squidwrd") def test_user_admin_get_tso_logon_procedure_returns_none_when_no_tso_segment_exists( @@ -906,7 +906,7 @@ def test_user_admin_get_tso_default_region_size_raises_an_exception_when_extract call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_default_region_size("squidwrd") def test_user_admin_get_default_tso_region_size_returns_none_when_no_tso_segment_exists( @@ -937,7 +937,7 @@ def test_user_admin_get_tso_sysout_class_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_sysout_class("squidwrd") def test_user_admin_get_tso_sysout_class_returns_none_when_no_tso_segment_exists( @@ -977,7 +977,7 @@ def test_user_admin_get_tso_user_data_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_user_data("squidwrd") def test_user_admin_get_tso_user_data_returns_none_when_no_tso_segment_exists( @@ -1019,7 +1019,7 @@ def test_user_admin_get_tso_data_set_allocation_unit_raises_an_exception_when_ex call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError): + with self.assertRaises(SecurityResponseError): self.user_admin.get_tso_data_set_allocation_unit("squidwrd") def test_user_admin_get_tso_data_set_allocation_unit_returns_none_when_no_tso_segment_exists( diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index 36457c8c..e6cd8ae6 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -10,6 +10,7 @@ AddOperationError, AlterOperationError, SecurityRequestError, + SecurityResponseError, UserAdmin, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -132,7 +133,7 @@ def test_user_admin_can_parse_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.user_admin.alter( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, @@ -177,7 +178,7 @@ def test_user_admin_can_parse_extract_user_base_omvs_error_xml( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.user_admin.extract("squidwrd", segments=["omvs"]) self.assertEqual( exception.exception.result, @@ -252,7 +253,7 @@ def test_user_admin_password_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSWORD ) @@ -298,7 +299,7 @@ def test_user_admin_passphrase_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSPHRASE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSPHRASE ) @@ -346,7 +347,7 @@ def test_user_admin_passphrase_and_password_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSPHRASE_AND_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSPHRASE_AND_PASSWORD ) @@ -395,7 +396,7 @@ def test_user_admin_password_message_not_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSWORD_SIMPLE ) @@ -436,7 +437,7 @@ def test_user_admin_can_parse_delete_user_error_xml( call_racf_mock.return_value = ( TestUserConstants.TEST_DELETE_USER_RESULT_ERROR_XML ) - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: self.user_admin.delete("squidwrd") self.assertEqual( exception.exception.result, @@ -479,7 +480,7 @@ def test_user_admin_custom_secret_redacted_on_error( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(SecurityResponseError) as exception: user_admin.alter( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, diff --git a/tests/user/user_result_samples/add_user_result_error.json b/tests/user/user_result_samples/add_user_result_error.json index 856455d3..e34ebd8b 100644 --- a/tests/user/user_result_samples/add_user_result_error.json +++ b/tests/user/user_result_samples/add_user_result_error.json @@ -4,19 +4,14 @@ "name": "squidward", "operation": "set", "requestId": "UserRequest", - "commands": [ - { - "safReturnCode": 10, - "returnCode": 2000, - "reasonCode": 68, - "textInError": "name", - "errorOffset": 149, - "messages": [ - "Invalid attribute value specified.", - "Please note that for any text in error, redacted values may skew offset calculations." - ] - } - ] + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "textInError": "name", + "errorOffset": 149, + "errorMessage": "Invalid attribute value specified." + } }, "returnCode": 2000, "reasonCode": 68 diff --git a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json index 233707c1..8583fa4e 100644 --- a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json +++ b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json @@ -4,19 +4,14 @@ "name": "squidward", "operation": "listdata", "requestId": "UserRequest", - "commands": [ - { - "safReturnCode": 10, - "returnCode": 2000, - "reasonCode": 68, - "textInError": "name", - "errorOffset": 149, - "messages": [ - "Invalid attribute value specified.", - "Please note that for any text in error, redacted values may skew offset calculations." - ] - } - ] + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "textInError": "name", + "errorOffset": 149, + "errorMessage": "Invalid attribute value specified." + } }, "returnCode": 2000, "reasonCode": 68 From 8a227dea097fe988cc2ed1c290b93962d268b74b Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Fri, 1 Dec 2023 10:48:08 -0500 Subject: [PATCH 13/30] add running_user even when not specified -Get current user on OS\390 systems to add to result dictionary -On non-OS\390 systems use placeholder "testuser" -add this value into all unit tests Signed-off-by: Elijah Swift --- pyracf/common/security_result.py | 12 ++++++++++++ .../access_log_samples/permit_access_error.log | 3 ++- .../access_log_samples/permit_access_success.log | 3 ++- .../delete_access_result_error.json | 3 ++- .../delete_access_result_success.json | 3 ++- .../permit_access_result_error.json | 3 ++- .../permit_access_result_success.json | 3 ++- .../connect_connection_error.log | 3 ++- .../connect_connection_success.log | 3 ++- .../connect_connection_result_error.json | 3 ++- .../connect_connection_result_success.json | 3 ++- .../delete_connection_result_error.json | 3 ++- .../delete_connection_result_success.json | 3 ++- .../data_set_log_samples/alter_data_set_error.log | 9 ++++++--- .../data_set_log_samples/alter_data_set_success.log | 9 ++++++--- .../extract_data_set_base_only_error.log | 3 ++- .../extract_data_set_base_only_success.log | 6 ++++-- .../add_data_set_result_error.json | 3 ++- .../add_data_set_result_generic_success.json | 3 ++- .../add_data_set_result_success.json | 3 ++- .../alter_data_set_result_error.json | 3 ++- .../alter_data_set_result_success.json | 3 ++- .../delete_data_set_result_error.json | 3 ++- .../delete_data_set_result_success.json | 3 ++- .../extract_data_set_result_bad_attribute_error.json | 3 ++- .../extract_data_set_result_base_only_error.json | 3 ++- .../extract_data_set_result_base_only_success.json | 3 ++- ...ct_data_set_result_generic_base_only_success.json | 3 ++- tests/group/group_log_samples/alter_group_error.log | 9 ++++++--- .../group/group_log_samples/alter_group_success.log | 9 ++++++--- .../extract_group_base_omvs_error.log | 3 ++- .../extract_group_base_omvs_success.log | 6 ++++-- .../group_result_samples/add_group_result_error.json | 3 ++- .../add_group_result_success.json | 3 ++- .../alter_group_result_error.json | 3 ++- .../alter_group_result_success.json | 3 ++- .../delete_group_result_error.json | 3 ++- .../delete_group_result_success.json | 3 ++- .../extract_group_result_bad_attribute_error.json | 3 ++- .../extract_group_result_base_omvs_error.json | 3 ++- .../extract_group_result_base_omvs_success.json | 3 ++- .../extract_group_result_base_only_error.json | 3 ++- .../extract_group_result_base_only_success.json | 3 ++- .../resource_log_samples/alter_resource_error.log | 9 ++++++--- .../resource_log_samples/alter_resource_success.log | 9 ++++++--- .../extract_resource_base_error.log | 3 ++- .../extract_resource_base_success.log | 6 ++++-- .../add_resource_result_error.json | 3 ++- ...d_resource_result_precheck_uacc_none_success.json | 3 ++- .../add_resource_result_success.json | 3 ++- .../alter_resource_result_error.json | 3 ++- .../alter_resource_result_success.json | 3 ++- .../delete_resource_result_error.json | 3 ++- .../delete_resource_result_success.json | 3 ++- .../extract_resource_result_bad_class_error.json | 3 ++- .../extract_resource_result_base_error.json | 3 ++- .../extract_resource_result_base_success.json | 3 ++- .../extract_resource_result_multi_base_success.json | 3 ++- .../setropts_log_samples/alter_setropts_error.log | 3 ++- .../setropts_log_samples/alter_setropts_success.log | 3 ++- .../setropts_log_samples/list_setropts_success.log | 6 ++++-- .../setropts_log_samples/list_setropts_success_2.log | 6 ++++-- .../setropts_log_samples/list_setropts_success_3.log | 6 ++++-- .../alter_setropts_result_error.json | 3 ++- .../alter_setropts_result_success.json | 3 ++- .../list_setropts_result_success.json | 3 ++- .../list_setropts_result_success_2.json | 3 ++- .../list_setropts_result_success_3.json | 3 ++- .../alter_user_additional_secret_added_error.log | 9 ++++++--- .../alter_user_additional_secret_added_success.log | 9 ++++++--- tests/user/user_log_samples/alter_user_error.log | 9 ++++++--- .../alter_user_passphrase_and_password_error.log | 9 ++++++--- .../alter_user_passphrase_and_password_success.log | 9 ++++++--- .../user_log_samples/alter_user_passphrase_error.log | 9 ++++++--- .../alter_user_passphrase_success.log | 9 ++++++--- .../user_log_samples/alter_user_password_error.log | 9 ++++++--- .../user_log_samples/alter_user_password_success.log | 9 ++++++--- tests/user/user_log_samples/alter_user_success.log | 9 ++++++--- .../extract_user_base_omvs_error.log | 3 ++- .../extract_user_base_omvs_success.log | 6 ++++-- .../user_result_samples/add_user_result_error.json | 3 ++- .../user_result_samples/add_user_result_success.json | 3 ++- .../user_result_samples/alter_user_result_error.json | 3 ++- .../alter_user_result_error_uid_secret.json | 3 ++- .../alter_user_result_extended_success.json | 3 ++- ...er_user_result_passphrase_and_password_error.json | 3 ++- ..._user_result_passphrase_and_password_success.json | 3 ++- .../alter_user_result_passphrase_error.json | 3 ++- .../alter_user_result_passphrase_success.json | 3 ++- .../alter_user_result_password_error.json | 3 ++- .../alter_user_result_password_success.json | 3 ++- .../alter_user_result_success.json | 3 ++- .../delete_user_result_error.json | 3 ++- .../delete_user_result_success.json | 3 ++- .../extract_user_result_bad_attribute_error.json | 3 ++- ...extract_user_result_base_omvs_csdata_success.json | 3 ++- .../extract_user_result_base_omvs_error.json | 3 ++- .../extract_user_result_base_omvs_success.json | 3 ++- ...ract_user_result_base_omvs_tso_revoke_resume.json | 3 ++- .../extract_user_result_base_only_error.json | 3 ++- .../extract_user_result_base_only_success.json | 3 ++- .../extract_user_result_extra_messages_success.json | 3 ++- 102 files changed, 292 insertions(+), 140 deletions(-) diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index f460c1cd..e371616d 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -1,5 +1,7 @@ """Generic Security Result Parser.""" +import getpass +import platform import re from typing import Union from xml.etree.ElementTree import Element # Only used for type hints. @@ -20,6 +22,10 @@ def __init__( self.__result_dictionary["securityResult"][ "runningUserid" ] = running_userid.lower() + else: + self.__result_dictionary["securityResult"][ + "runningUserid" + ] = self.__get_current_userid() def __extract_results(self) -> None: """Extract XML results into a dictionary.""" @@ -137,6 +143,12 @@ def __to_pascal_case(self, key: str) -> str: case _: return key + def __get_current_userid(self) -> str: + if platform.system() == "OS/390": + return getpass.getuser().lower() + else: + return "testuser" + def get_result_dictionary(self) -> dict: """Return result dictionary.""" return self.__result_dictionary diff --git a/tests/access/access_log_samples/permit_access_error.log b/tests/access/access_log_samples/permit_access_error.log index 28151cbf..a045350e 100644 --- a/tests/access/access_log_samples/permit_access_error.log +++ b/tests/access/access_log_samples/permit_access_error.log @@ -77,7 +77,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/access/access_log_samples/permit_access_success.log b/tests/access/access_log_samples/permit_access_success.log index d82660a9..46976dfa 100644 --- a/tests/access/access_log_samples/permit_access_success.log +++ b/tests/access/access_log_samples/permit_access_success.log @@ -73,7 +73,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/access/access_result_samples/delete_access_result_error.json b/tests/access/access_result_samples/delete_access_result_error.json index 13cb28c9..009fd49a 100644 --- a/tests/access/access_result_samples/delete_access_result_error.json +++ b/tests/access/access_result_samples/delete_access_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/access/access_result_samples/delete_access_result_success.json b/tests/access/access_result_samples/delete_access_result_success.json index d21fb4d1..40f76904 100644 --- a/tests/access/access_result_samples/delete_access_result_success.json +++ b/tests/access/access_result_samples/delete_access_result_success.json @@ -18,6 +18,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/access/access_result_samples/permit_access_result_error.json b/tests/access/access_result_samples/permit_access_result_error.json index 9c13abf1..2e1cf32f 100644 --- a/tests/access/access_result_samples/permit_access_result_error.json +++ b/tests/access/access_result_samples/permit_access_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/access/access_result_samples/permit_access_result_success.json b/tests/access/access_result_samples/permit_access_result_success.json index 594d4204..af3628c5 100644 --- a/tests/access/access_result_samples/permit_access_result_success.json +++ b/tests/access/access_result_samples/permit_access_result_success.json @@ -15,6 +15,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/connection/connection_log_samples/connect_connection_error.log b/tests/connection/connection_log_samples/connect_connection_error.log index c7473430..1563c4bc 100644 --- a/tests/connection/connection_log_samples/connect_connection_error.log +++ b/tests/connection/connection_log_samples/connect_connection_error.log @@ -79,7 +79,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/connection/connection_log_samples/connect_connection_success.log b/tests/connection/connection_log_samples/connect_connection_success.log index f7782fa8..c10b7e01 100644 --- a/tests/connection/connection_log_samples/connect_connection_success.log +++ b/tests/connection/connection_log_samples/connect_connection_success.log @@ -73,7 +73,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } 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 00882176..fe180153 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_error.json +++ b/tests/connection/connection_result_samples/connect_connection_result_error.json @@ -19,6 +19,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 00863adc..75d10ecc 100644 --- a/tests/connection/connection_result_samples/connect_connection_result_success.json +++ b/tests/connection/connection_result_samples/connect_connection_result_success.json @@ -15,6 +15,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 38e894a6..193ad0f6 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_error.json +++ b/tests/connection/connection_result_samples/delete_connection_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 4983b418..0b4a3d20 100644 --- a/tests/connection/connection_result_samples/delete_connection_result_success.json +++ b/tests/connection/connection_result_samples/delete_connection_result_success.json @@ -15,6 +15,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 e637e65f..90cd31b1 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 @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -158,7 +159,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -259,7 +261,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/data_set/data_set_log_samples/alter_data_set_success.log b/tests/data_set/data_set_log_samples/alter_data_set_success.log index 131978a6..b76bc4af 100644 --- a/tests/data_set/data_set_log_samples/alter_data_set_success.log +++ b/tests/data_set/data_set_log_samples/alter_data_set_success.log @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -158,7 +159,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -243,7 +245,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log b/tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log index 2e791411..ced4ca62 100644 --- a/tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log +++ b/tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log @@ -63,7 +63,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log b/tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log index 68c4fd2f..1cec2fa0 100644 --- a/tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log +++ b/tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -158,7 +159,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/data_set/data_set_result_samples/add_data_set_result_error.json b/tests/data_set/data_set_result_samples/add_data_set_result_error.json index ed2fc26a..a3891653 100644 --- a/tests/data_set/data_set_result_samples/add_data_set_result_error.json +++ b/tests/data_set/data_set_result_samples/add_data_set_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/add_data_set_result_generic_success.json b/tests/data_set/data_set_result_samples/add_data_set_result_generic_success.json index f3c48657..927b7725 100644 --- a/tests/data_set/data_set_result_samples/add_data_set_result_generic_success.json +++ b/tests/data_set/data_set_result_samples/add_data_set_result_generic_success.json @@ -21,6 +21,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/add_data_set_result_success.json b/tests/data_set/data_set_result_samples/add_data_set_result_success.json index 78dfe698..545e29db 100644 --- a/tests/data_set/data_set_result_samples/add_data_set_result_success.json +++ b/tests/data_set/data_set_result_samples/add_data_set_result_success.json @@ -21,6 +21,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/alter_data_set_result_error.json b/tests/data_set/data_set_result_samples/alter_data_set_result_error.json index d4ddbf7f..218ee931 100644 --- a/tests/data_set/data_set_result_samples/alter_data_set_result_error.json +++ b/tests/data_set/data_set_result_samples/alter_data_set_result_error.json @@ -27,6 +27,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "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.json b/tests/data_set/data_set_result_samples/alter_data_set_result_success.json index 58e5faf2..511eb54c 100644 --- a/tests/data_set/data_set_result_samples/alter_data_set_result_success.json +++ b/tests/data_set/data_set_result_samples/alter_data_set_result_success.json @@ -18,6 +18,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/delete_data_set_result_error.json b/tests/data_set/data_set_result_samples/delete_data_set_result_error.json index d17658da..62f9681b 100644 --- a/tests/data_set/data_set_result_samples/delete_data_set_result_error.json +++ b/tests/data_set/data_set_result_samples/delete_data_set_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/delete_data_set_result_success.json b/tests/data_set/data_set_result_samples/delete_data_set_result_success.json index 0af42c0e..3b5f86ba 100644 --- a/tests/data_set/data_set_result_samples/delete_data_set_result_success.json +++ b/tests/data_set/data_set_result_samples/delete_data_set_result_success.json @@ -15,6 +15,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_bad_attribute_error.json b/tests/data_set/data_set_result_samples/extract_data_set_result_bad_attribute_error.json index b5d84c93..dc2463ae 100644 --- a/tests/data_set/data_set_result_samples/extract_data_set_result_bad_attribute_error.json +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_bad_attribute_error.json @@ -20,6 +20,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json index cdee6661..cfe352b3 100644 --- a/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json index 5165ab4c..ede27465 100644 --- a/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json @@ -39,6 +39,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json b/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json index 28daeab4..71e9f714 100644 --- a/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json @@ -36,6 +36,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/group/group_log_samples/alter_group_error.log b/tests/group/group_log_samples/alter_group_error.log index 8b1ad45c..34205d68 100644 --- a/tests/group/group_log_samples/alter_group_error.log +++ b/tests/group/group_log_samples/alter_group_error.log @@ -86,7 +86,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -150,7 +151,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -237,7 +239,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/group/group_log_samples/alter_group_success.log b/tests/group/group_log_samples/alter_group_success.log index 1e1d1365..7bf5e943 100644 --- a/tests/group/group_log_samples/alter_group_success.log +++ b/tests/group/group_log_samples/alter_group_success.log @@ -86,7 +86,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -150,7 +151,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -229,7 +231,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } 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 baafe74c..cb969528 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 @@ -66,7 +66,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } 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 7ab0fd76..fcf3adde 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 @@ -86,7 +86,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -130,7 +131,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/group/group_result_samples/add_group_result_error.json b/tests/group/group_result_samples/add_group_result_error.json index cf98e80f..6500121b 100644 --- a/tests/group/group_result_samples/add_group_result_error.json +++ b/tests/group/group_result_samples/add_group_result_error.json @@ -14,6 +14,7 @@ } }, "returnCode": 2000, - "reasonCode": 68 + "reasonCode": 68, + "runningUserid": "testuser" } } \ No newline at end of file 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 08b867ae..1ae3e4bb 100644 --- a/tests/group/group_result_samples/add_group_result_success.json +++ b/tests/group/group_result_samples/add_group_result_success.json @@ -20,6 +20,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 69479139..77d89a27 100644 --- a/tests/group/group_result_samples/alter_group_result_error.json +++ b/tests/group/group_result_samples/alter_group_result_error.json @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 1ca2aa4a..19a411f6 100644 --- a/tests/group/group_result_samples/alter_group_result_success.json +++ b/tests/group/group_result_samples/alter_group_result_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 ba44ec86..afefddae 100644 --- a/tests/group/group_result_samples/delete_group_result_error.json +++ b/tests/group/group_result_samples/delete_group_result_error.json @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 7e373842..13ff5a4e 100644 --- a/tests/group/group_result_samples/delete_group_result_success.json +++ b/tests/group/group_result_samples/delete_group_result_success.json @@ -14,6 +14,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json index 5dfa55b7..7bbc640c 100644 --- a/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json +++ b/tests/group/group_result_samples/extract_group_result_bad_attribute_error.json @@ -14,6 +14,7 @@ } }, "returnCode": 2000, - "reasonCode": 68 + "reasonCode": 68, + "runningUserid": "testuser" } } \ No newline at end of file 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 07330edf..745d18f8 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 @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 fddbcbf4..2b57fad5 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 @@ -32,6 +32,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 3d696bf3..0881b1fb 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 @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 9323d833..8ae9b377 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 @@ -52,6 +52,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_log_samples/alter_resource_error.log b/tests/resource/resource_log_samples/alter_resource_error.log index 922c6157..cb8b9b79 100644 --- a/tests/resource/resource_log_samples/alter_resource_error.log +++ b/tests/resource/resource_log_samples/alter_resource_error.log @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -154,7 +155,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -247,7 +249,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/resource/resource_log_samples/alter_resource_success.log b/tests/resource/resource_log_samples/alter_resource_success.log index 5f5a6f9b..9a960990 100644 --- a/tests/resource/resource_log_samples/alter_resource_success.log +++ b/tests/resource/resource_log_samples/alter_resource_success.log @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -154,7 +155,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -243,7 +245,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/resource/resource_log_samples/extract_resource_base_error.log b/tests/resource/resource_log_samples/extract_resource_base_error.log index 077b1b20..e4b44174 100644 --- a/tests/resource/resource_log_samples/extract_resource_base_error.log +++ b/tests/resource/resource_log_samples/extract_resource_base_error.log @@ -63,7 +63,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/resource/resource_log_samples/extract_resource_base_success.log b/tests/resource/resource_log_samples/extract_resource_base_success.log index 63625d61..b521d899 100644 --- a/tests/resource/resource_log_samples/extract_resource_base_success.log +++ b/tests/resource/resource_log_samples/extract_resource_base_success.log @@ -107,7 +107,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -154,7 +155,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/resource/resource_result_samples/add_resource_result_error.json b/tests/resource/resource_result_samples/add_resource_result_error.json index 8eb303da..3ea06721 100644 --- a/tests/resource/resource_result_samples/add_resource_result_error.json +++ b/tests/resource/resource_result_samples/add_resource_result_error.json @@ -30,6 +30,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json index 135b5417..14731480 100644 --- a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json +++ b/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json @@ -27,6 +27,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/add_resource_result_success.json b/tests/resource/resource_result_samples/add_resource_result_success.json index 3427b256..996e945e 100644 --- a/tests/resource/resource_result_samples/add_resource_result_success.json +++ b/tests/resource/resource_result_samples/add_resource_result_success.json @@ -27,6 +27,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/alter_resource_result_error.json b/tests/resource/resource_result_samples/alter_resource_result_error.json index 54f8f39e..8d7e21f1 100644 --- a/tests/resource/resource_result_samples/alter_resource_result_error.json +++ b/tests/resource/resource_result_samples/alter_resource_result_error.json @@ -23,6 +23,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/alter_resource_result_success.json b/tests/resource/resource_result_samples/alter_resource_result_success.json index 6e939576..f42229c4 100644 --- a/tests/resource/resource_result_samples/alter_resource_result_success.json +++ b/tests/resource/resource_result_samples/alter_resource_result_success.json @@ -21,6 +21,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/delete_resource_result_error.json b/tests/resource/resource_result_samples/delete_resource_result_error.json index 249de09e..d087012e 100644 --- a/tests/resource/resource_result_samples/delete_resource_result_error.json +++ b/tests/resource/resource_result_samples/delete_resource_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/delete_resource_result_success.json b/tests/resource/resource_result_samples/delete_resource_result_success.json index ce780202..ee02d11e 100644 --- a/tests/resource/resource_result_samples/delete_resource_result_success.json +++ b/tests/resource/resource_result_samples/delete_resource_result_success.json @@ -18,6 +18,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_bad_class_error.json b/tests/resource/resource_result_samples/extract_resource_result_bad_class_error.json index dde77d51..3ded6f42 100644 --- a/tests/resource/resource_result_samples/extract_resource_result_bad_class_error.json +++ b/tests/resource/resource_result_samples/extract_resource_result_bad_class_error.json @@ -20,6 +20,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_base_error.json b/tests/resource/resource_result_samples/extract_resource_result_base_error.json index ed43a015..4e720da6 100644 --- a/tests/resource/resource_result_samples/extract_resource_result_base_error.json +++ b/tests/resource/resource_result_samples/extract_resource_result_base_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_base_success.json b/tests/resource/resource_result_samples/extract_resource_result_base_success.json index b605727b..0a07ed06 100644 --- a/tests/resource/resource_result_samples/extract_resource_result_base_success.json +++ b/tests/resource/resource_result_samples/extract_resource_result_base_success.json @@ -35,6 +35,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_multi_base_success.json b/tests/resource/resource_result_samples/extract_resource_result_multi_base_success.json index 395494ec..4013eeed 100644 --- a/tests/resource/resource_result_samples/extract_resource_result_multi_base_success.json +++ b/tests/resource/resource_result_samples/extract_resource_result_multi_base_success.json @@ -55,6 +55,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/setropts/setropts_log_samples/alter_setropts_error.log b/tests/setropts/setropts_log_samples/alter_setropts_error.log index 246e426c..73318452 100644 --- a/tests/setropts/setropts_log_samples/alter_setropts_error.log +++ b/tests/setropts/setropts_log_samples/alter_setropts_error.log @@ -74,7 +74,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/setropts/setropts_log_samples/alter_setropts_success.log b/tests/setropts/setropts_log_samples/alter_setropts_success.log index 7bd71b7a..14f7da8a 100644 --- a/tests/setropts/setropts_log_samples/alter_setropts_success.log +++ b/tests/setropts/setropts_log_samples/alter_setropts_success.log @@ -70,7 +70,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/setropts/setropts_log_samples/list_setropts_success.log b/tests/setropts/setropts_log_samples/list_setropts_success.log index 90eea0fb..ad81e599 100644 --- a/tests/setropts/setropts_log_samples/list_setropts_success.log +++ b/tests/setropts/setropts_log_samples/list_setropts_success.log @@ -330,7 +330,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -972,7 +973,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/setropts/setropts_log_samples/list_setropts_success_2.log b/tests/setropts/setropts_log_samples/list_setropts_success_2.log index 5676a8a7..50dd9443 100644 --- a/tests/setropts/setropts_log_samples/list_setropts_success_2.log +++ b/tests/setropts/setropts_log_samples/list_setropts_success_2.log @@ -248,7 +248,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -578,7 +579,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/setropts/setropts_log_samples/list_setropts_success_3.log b/tests/setropts/setropts_log_samples/list_setropts_success_3.log index c27d8df3..7888880f 100644 --- a/tests/setropts/setropts_log_samples/list_setropts_success_3.log +++ b/tests/setropts/setropts_log_samples/list_setropts_success_3.log @@ -598,7 +598,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -2034,7 +2035,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/setropts/setropts_result_samples/alter_setropts_result_error.json b/tests/setropts/setropts_result_samples/alter_setropts_result_error.json index be701b2c..dda84501 100644 --- a/tests/setropts/setropts_result_samples/alter_setropts_result_error.json +++ b/tests/setropts/setropts_result_samples/alter_setropts_result_error.json @@ -18,6 +18,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/setropts/setropts_result_samples/alter_setropts_result_success.json b/tests/setropts/setropts_result_samples/alter_setropts_result_success.json index 555eacc8..1911029e 100644 --- a/tests/setropts/setropts_result_samples/alter_setropts_result_success.json +++ b/tests/setropts/setropts_result_samples/alter_setropts_result_success.json @@ -16,6 +16,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/setropts/setropts_result_samples/list_setropts_result_success.json b/tests/setropts/setropts_result_samples/list_setropts_result_success.json index 48d0e4ae..1539c14c 100644 --- a/tests/setropts/setropts_result_samples/list_setropts_result_success.json +++ b/tests/setropts/setropts_result_samples/list_setropts_result_success.json @@ -630,6 +630,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/setropts/setropts_result_samples/list_setropts_result_success_2.json b/tests/setropts/setropts_result_samples/list_setropts_result_success_2.json index e118dc3e..7dd230a2 100644 --- a/tests/setropts/setropts_result_samples/list_setropts_result_success_2.json +++ b/tests/setropts/setropts_result_samples/list_setropts_result_success_2.json @@ -318,6 +318,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/setropts/setropts_result_samples/list_setropts_result_success_3.json b/tests/setropts/setropts_result_samples/list_setropts_result_success_3.json index 7dc8ff10..bc6e0b35 100644 --- a/tests/setropts/setropts_result_samples/list_setropts_result_success_3.json +++ b/tests/setropts/setropts_result_samples/list_setropts_result_success_3.json @@ -1424,6 +1424,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file 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 4a3f324b..2e02e6c4 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 @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -277,7 +279,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_additional_secret_added_success.log b/tests/user/user_log_samples/alter_user_additional_secret_added_success.log index 36746060..1ac51538 100644 --- a/tests/user/user_log_samples/alter_user_additional_secret_added_success.log +++ b/tests/user/user_log_samples/alter_user_additional_secret_added_success.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -269,7 +271,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_error.log b/tests/user/user_log_samples/alter_user_error.log index d73d98a3..b7a36eb6 100644 --- a/tests/user/user_log_samples/alter_user_error.log +++ b/tests/user/user_log_samples/alter_user_error.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -277,7 +279,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_passphrase_and_password_error.log b/tests/user/user_log_samples/alter_user_passphrase_and_password_error.log index bdab5be8..37da0cac 100644 --- a/tests/user/user_log_samples/alter_user_passphrase_and_password_error.log +++ b/tests/user/user_log_samples/alter_user_passphrase_and_password_error.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -287,7 +289,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_passphrase_and_password_success.log b/tests/user/user_log_samples/alter_user_passphrase_and_password_success.log index 1f9589a3..3f062048 100644 --- a/tests/user/user_log_samples/alter_user_passphrase_and_password_success.log +++ b/tests/user/user_log_samples/alter_user_passphrase_and_password_success.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -279,7 +281,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_passphrase_error.log b/tests/user/user_log_samples/alter_user_passphrase_error.log index 21a29802..49998753 100644 --- a/tests/user/user_log_samples/alter_user_passphrase_error.log +++ b/tests/user/user_log_samples/alter_user_passphrase_error.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -282,7 +284,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_passphrase_success.log b/tests/user/user_log_samples/alter_user_passphrase_success.log index 036f42b0..822a202f 100644 --- a/tests/user/user_log_samples/alter_user_passphrase_success.log +++ b/tests/user/user_log_samples/alter_user_passphrase_success.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -274,7 +276,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_password_error.log b/tests/user/user_log_samples/alter_user_password_error.log index ffc81d57..50124960 100644 --- a/tests/user/user_log_samples/alter_user_password_error.log +++ b/tests/user/user_log_samples/alter_user_password_error.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -282,7 +284,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_password_success.log b/tests/user/user_log_samples/alter_user_password_success.log index 5b51838c..a2c61cfa 100644 --- a/tests/user/user_log_samples/alter_user_password_success.log +++ b/tests/user/user_log_samples/alter_user_password_success.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -274,7 +276,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/alter_user_success.log b/tests/user/user_log_samples/alter_user_success.log index 5cd64554..b6319288 100644 --- a/tests/user/user_log_samples/alter_user_success.log +++ b/tests/user/user_log_samples/alter_user_success.log @@ -98,7 +98,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -161,7 +162,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -254,7 +256,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/extract_user_base_omvs_error.log b/tests/user/user_log_samples/extract_user_base_omvs_error.log index e88166fd..7112bf11 100644 --- a/tests/user/user_log_samples/extract_user_base_omvs_error.log +++ b/tests/user/user_log_samples/extract_user_base_omvs_error.log @@ -66,7 +66,8 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_log_samples/extract_user_base_omvs_success.log b/tests/user/user_log_samples/extract_user_base_omvs_success.log index 1e82f6e9..bf86b4e3 100644 --- a/tests/user/user_log_samples/extract_user_base_omvs_success.log +++ b/tests/user/user_log_samples/extract_user_base_omvs_success.log @@ -126,7 +126,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } @@ -202,7 +203,8 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_result_samples/add_user_result_error.json b/tests/user/user_result_samples/add_user_result_error.json index e34ebd8b..43f7ae97 100644 --- a/tests/user/user_result_samples/add_user_result_error.json +++ b/tests/user/user_result_samples/add_user_result_error.json @@ -14,6 +14,7 @@ } }, "returnCode": 2000, - "reasonCode": 68 + "reasonCode": 68, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/add_user_result_success.json b/tests/user/user_result_samples/add_user_result_success.json index 157ce959..d9948401 100644 --- a/tests/user/user_result_samples/add_user_result_success.json +++ b/tests/user/user_result_samples/add_user_result_success.json @@ -23,6 +23,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_error.json b/tests/user/user_result_samples/alter_user_result_error.json index e387aea5..41c6bea0 100644 --- a/tests/user/user_result_samples/alter_user_result_error.json +++ b/tests/user/user_result_samples/alter_user_result_error.json @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } 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 4aef456c..7bfd2100 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 @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } diff --git a/tests/user/user_result_samples/alter_user_result_extended_success.json b/tests/user/user_result_samples/alter_user_result_extended_success.json index 926a9785..1e35ff47 100644 --- a/tests/user/user_result_samples/alter_user_result_extended_success.json +++ b/tests/user/user_result_samples/alter_user_result_extended_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_passphrase_and_password_error.json b/tests/user/user_result_samples/alter_user_result_passphrase_and_password_error.json index d937bb8e..1e500c26 100644 --- a/tests/user/user_result_samples/alter_user_result_passphrase_and_password_error.json +++ b/tests/user/user_result_samples/alter_user_result_passphrase_and_password_error.json @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_passphrase_and_password_success.json b/tests/user/user_result_samples/alter_user_result_passphrase_and_password_success.json index bdbb9d44..11e1006c 100644 --- a/tests/user/user_result_samples/alter_user_result_passphrase_and_password_success.json +++ b/tests/user/user_result_samples/alter_user_result_passphrase_and_password_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_passphrase_error.json b/tests/user/user_result_samples/alter_user_result_passphrase_error.json index 5b067f5c..ca3927c2 100644 --- a/tests/user/user_result_samples/alter_user_result_passphrase_error.json +++ b/tests/user/user_result_samples/alter_user_result_passphrase_error.json @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_passphrase_success.json b/tests/user/user_result_samples/alter_user_result_passphrase_success.json index 153669eb..b351e894 100644 --- a/tests/user/user_result_samples/alter_user_result_passphrase_success.json +++ b/tests/user/user_result_samples/alter_user_result_passphrase_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_password_error.json b/tests/user/user_result_samples/alter_user_result_password_error.json index b2792339..83761c1c 100644 --- a/tests/user/user_result_samples/alter_user_result_password_error.json +++ b/tests/user/user_result_samples/alter_user_result_password_error.json @@ -22,6 +22,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_password_success.json b/tests/user/user_result_samples/alter_user_result_password_success.json index 44dcb6a3..63945d16 100644 --- a/tests/user/user_result_samples/alter_user_result_password_success.json +++ b/tests/user/user_result_samples/alter_user_result_password_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/alter_user_result_success.json b/tests/user/user_result_samples/alter_user_result_success.json index 70cc68c6..290032c6 100644 --- a/tests/user/user_result_samples/alter_user_result_success.json +++ b/tests/user/user_result_samples/alter_user_result_success.json @@ -17,6 +17,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/delete_user_result_error.json b/tests/user/user_result_samples/delete_user_result_error.json index b18d2e08..5fbb870e 100644 --- a/tests/user/user_result_samples/delete_user_result_error.json +++ b/tests/user/user_result_samples/delete_user_result_error.json @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/delete_user_result_success.json b/tests/user/user_result_samples/delete_user_result_success.json index d4c214bd..9b297980 100644 --- a/tests/user/user_result_samples/delete_user_result_success.json +++ b/tests/user/user_result_samples/delete_user_result_success.json @@ -14,6 +14,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json index 8583fa4e..71233b35 100644 --- a/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json +++ b/tests/user/user_result_samples/extract_user_result_bad_attribute_error.json @@ -14,6 +14,7 @@ } }, "returnCode": 2000, - "reasonCode": 68 + "reasonCode": 68, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json b/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json index 48671a57..160740d7 100644 --- a/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json +++ b/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json @@ -64,6 +64,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_error.json b/tests/user/user_result_samples/extract_user_result_base_omvs_error.json index 31ea4ca0..b6c49a8c 100644 --- a/tests/user/user_result_samples/extract_user_result_base_omvs_error.json +++ b/tests/user/user_result_samples/extract_user_result_base_omvs_error.json @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_success.json b/tests/user/user_result_samples/extract_user_result_base_omvs_success.json index c007bcc9..42d1a19d 100644 --- a/tests/user/user_result_samples/extract_user_result_base_omvs_success.json +++ b/tests/user/user_result_samples/extract_user_result_base_omvs_success.json @@ -64,6 +64,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_tso_revoke_resume.json b/tests/user/user_result_samples/extract_user_result_base_omvs_tso_revoke_resume.json index c035e486..f12c6d97 100644 --- a/tests/user/user_result_samples/extract_user_result_base_omvs_tso_revoke_resume.json +++ b/tests/user/user_result_samples/extract_user_result_base_omvs_tso_revoke_resume.json @@ -76,6 +76,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_only_error.json b/tests/user/user_result_samples/extract_user_result_base_only_error.json index 7edd69fd..8de62923 100644 --- a/tests/user/user_result_samples/extract_user_result_base_only_error.json +++ b/tests/user/user_result_samples/extract_user_result_base_only_error.json @@ -17,6 +17,7 @@ ] }, "returnCode": 4, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_base_only_success.json b/tests/user/user_result_samples/extract_user_result_base_only_success.json index db9bb8bc..20ffb3b5 100644 --- a/tests/user/user_result_samples/extract_user_result_base_only_success.json +++ b/tests/user/user_result_samples/extract_user_result_base_only_success.json @@ -51,6 +51,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file diff --git a/tests/user/user_result_samples/extract_user_result_extra_messages_success.json b/tests/user/user_result_samples/extract_user_result_extra_messages_success.json index fec77e9c..6f836ece 100644 --- a/tests/user/user_result_samples/extract_user_result_extra_messages_success.json +++ b/tests/user/user_result_samples/extract_user_result_extra_messages_success.json @@ -51,6 +51,7 @@ ] }, "returnCode": 0, - "reasonCode": 0 + "reasonCode": 0, + "runningUserid": "testuser" } } \ No newline at end of file From 000b71fced804e7c567f29a411bbb2b6abd40790 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Dec 2023 11:04:33 -0500 Subject: [PATCH 14/30] Name Changes -Name and formatting changes -Add userid to result dictionary no matter what Signed-off-by: Elijah Swift --- pyracf/__init__.py | 4 +- pyracf/common/downstream_fatal_error.py | 66 +++++++++++++++++ pyracf/common/security_admin.py | 27 +++---- pyracf/common/security_request_error.py | 74 ++++++------------- pyracf/common/security_response_error.py | 34 --------- pyracf/common/security_result.py | 9 +-- pyracf/data_set/data_set_admin.py | 6 +- pyracf/group/group_admin.py | 6 +- pyracf/resource/resource_admin.py | 6 +- ...{install_precheck.py => setup_precheck.py} | 6 +- pyracf/user/user_admin.py | 6 +- tests/access/test_access_debug_logging.py | 4 +- tests/access/test_access_result_parser.py | 11 +-- tests/common/test_common_constants.py | 6 +- tests/common/test_install_precheck_script.py | 14 ++-- tests/common/test_security_request_error.py | 10 +-- .../test_connection_debug_logging.py | 4 +- .../test_connection_result_parser.py | 6 +- tests/data_set/test_data_set_debug_logging.py | 6 +- tests/data_set/test_data_set_getters.py | 6 +- tests/data_set/test_data_set_result_parser.py | 10 +-- tests/group/test_group_debug_logging.py | 6 +- tests/group/test_group_getters.py | 14 ++-- tests/group/test_group_result_parser.py | 10 +-- tests/resource/test_resource_debug_logging.py | 6 +- tests/resource/test_resource_getters.py | 6 +- tests/resource/test_resource_result_parser.py | 10 +-- .../test_resource_subfunction_extracts.py | 14 ++-- tests/setropts/test_setropts_debug_logging.py | 4 +- tests/setropts/test_setropts_getters.py | 6 +- tests/setropts/test_setropts_result_parser.py | 6 +- tests/test_runner.py | 7 +- tests/user/test_user_debug_logging.py | 16 ++-- tests/user/test_user_getters.py | 52 ++++++------- tests/user/test_user_result_parser.py | 20 ++--- 35 files changed, 243 insertions(+), 255 deletions(-) create mode 100644 pyracf/common/downstream_fatal_error.py delete mode 100644 pyracf/common/security_response_error.py rename pyracf/scripts/{install_precheck.py => setup_precheck.py} (92%) diff --git a/pyracf/__init__.py b/pyracf/__init__.py index 97a9357a..e99b15f6 100644 --- a/pyracf/__init__.py +++ b/pyracf/__init__.py @@ -2,8 +2,8 @@ from .access.access_admin import AccessAdmin from .common.add_operation_error import AddOperationError from .common.alter_operation_error import AlterOperationError +from .common.downstream_fatal_error import DownstreamFatalError from .common.security_request_error import SecurityRequestError -from .common.security_response_error import SecurityResponseError from .common.segment_error import SegmentError from .common.segment_trait_error import SegmentTraitError from .common.userid_error import UserIdError @@ -11,6 +11,6 @@ from .data_set.data_set_admin import DataSetAdmin from .group.group_admin import GroupAdmin from .resource.resource_admin import ResourceAdmin -from .scripts.install_precheck import define_precheck_profile +from .scripts.setup_precheck import setup_precheck from .setropts.setropts_admin import SetroptsAdmin from .user.user_admin import UserAdmin diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py new file mode 100644 index 00000000..c3882837 --- /dev/null +++ b/pyracf/common/downstream_fatal_error.py @@ -0,0 +1,66 @@ +"""Exception to use when no data is returned by IRRSMO00.""" +from typing import Union + + +class DownstreamFatalError(Exception): + """ + Raised when no xml string is returned by IRRSMO00. + """ + + def __init__( + self, + xml_str: str, + request_xml: bytes, + run_as_userid: Union[str, None] = None, + result_dict: dict = None, + ) -> None: + self.message = "Security request made to IRRSMO00 failed." + self.saf_return_code = xml_str[0] + self.racf_return_code = xml_str[1] + self.racf_reason_code = xml_str[2] + self.request_xml = request_xml.decode("utf-8") + self.message += ( + f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" + + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" + ) + if result_dict is not None: + self.message += ( + "\n\nSee results dictionary " + + f"'{self.__class__.__name__}.result' for more details.\n" + + "\n\nYou can also check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" + ) + self.result = result_dict + elif ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 16) + ): + self.message += ( + "\n\nCheck to see if the proper RACF permissions are in place.\n" + + "For `set` or `alter` functions, you must have at least READ " + + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + ) + elif ( + (self.saf_return_code == 8) + and (self.racf_return_code == 200) + and (self.racf_reason_code == 8) + ): + self.message += ( + "\n\nCheck to see if the proper RACF permissions are in place.\n" + + "For the `run_as_userid` feature, you must have at least UPDATE" + + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." + ) + else: + self.message += ( + "\n\nPlease check the specified return and reason codes against" + + " the IRRSMO00 documented return and reason codes for more information" + + " about this error.\n" + + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" + ) + self.message = f"({self.__class__.__name__}) {self.message}" + + def __str__(self) -> str: + return self.message diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 35a838c4..3d1b957a 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -5,11 +5,11 @@ from datetime import datetime from typing import Any, List, Tuple, Union +from .downstream_fatal_error import DownstreamFatalError from .irrsmo00 import IRRSMO00 from .logger import Logger from .security_request import SecurityRequest from .security_request_error import SecurityRequestError -from .security_response_error import SecurityResponseError from .security_result import SecurityResult from .segment_error import SegmentError from .segment_trait_error import SegmentTraitError @@ -92,14 +92,9 @@ def set_running_userid(self, new_userid: Union[str, None]) -> None: if new_userid is None: self.__running_userid = new_userid return - if ( - isinstance(new_userid, str) - and (len(new_userid) <= 8) - and (not new_userid == "") - ): - self.__running_userid = new_userid.upper() - return - raise UserIdError(new_userid) + if not isinstance(new_userid, str) or len(new_userid) > 8 or new_userid == "": + raise UserIdError(new_userid) + self.__running_userid = new_userid.upper() def clear_running_userid(self) -> None: self.__running_userid = None @@ -203,8 +198,8 @@ def _make_request( if isinstance(result_xml, list): # When IRRSMO00 encounters some errors, it returns no XML response string. # When this happens, the c code instead surfaces the return and reason - # codes which causes a SecurityRequestError to be raised. - raise SecurityRequestError( + # codes which causes a DownstreamFatalError to be raised. + raise DownstreamFatalError( result_xml, request_xml, self.get_running_userid() ) if self.__debug: @@ -223,9 +218,9 @@ def _make_request( # All return codes greater than 4 are indicative of an issue with # IRRSMO00 that would stop a command image from being generated. # The user should interogate the result dictionary attached to the - # SecurityResponseError as well as the return and reason codes to + # SecurityRequestError as well as the return and reason codes to # resolve the problem. - raise SecurityRequestError( + raise DownstreamFatalError( [ 8, result_dictionary["securityResult"]["returnCode"], @@ -236,12 +231,12 @@ def _make_request( result_dictionary, ) if result_dictionary["securityResult"]["returnCode"] != 0: - # All non-zero return codes should cause a SecurityResponseError to be raised. + # All non-zero return codes should cause a SecurityRequestError to be raised. # Even if a return code of 4 is not indicative of a problem, it it is # up to the user to interogate the result dictionary attached to the - # SecurityResponseError and decided whether or not the return code 4 is + # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. - raise SecurityResponseError(result_dictionary) + raise SecurityRequestError(result_dictionary) return result_dictionary def _to_steps(self, results: Union[List[dict], dict, bytes]) -> Union[dict, bytes]: diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 656fa6b8..56d30d48 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -1,66 +1,34 @@ -"""Exception to use when no data is returned by IRRSMO00.""" -from typing import Union +"""Exception to use when data returned by IRRSMO00 indicates that the request failed.""" class SecurityRequestError(Exception): """ - Raised when no xml string is returned by IRRSMO00. + Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. """ - def __init__( - self, - xml_str: str, - request_xml: bytes, - run_as_userid: Union[str, None] = None, - result_dict: dict = None, - ) -> None: + def __init__(self, result: dict) -> None: self.message = "Security request made to IRRSMO00 failed." - self.saf_return_code = xml_str[0] - self.racf_return_code = xml_str[1] - self.racf_reason_code = xml_str[2] - self.request_xml = request_xml.decode("utf-8") + self.result = result self.message += ( - f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" - + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" + "\n\nSee results dictionary " + + f"'{self.__class__.__name__}.result' for more details." ) - if result_dict is not None: - self.message += ( - "\n\nSee results dictionary " - + f"'{self.__class__.__name__}.result' for more details.\n" - + "\n\nYou can also check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" - + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" - ) - self.result = result_dict - elif ( - (self.saf_return_code == 8) - and (self.racf_return_code == 200) - and (self.racf_reason_code == 16) - ): - self.message += ( - "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." - ) - elif ( - (self.saf_return_code == 8) - and (self.racf_return_code == 200) - and (self.racf_reason_code == 8) - ): - self.message += ( - "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE" - + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." - ) - else: - self.message += ( - "\n\nPlease check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" - + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" - ) self.message = f"({self.__class__.__name__}) {self.message}" def __str__(self) -> str: return self.message + + def contains_error_message( + self, security_definition_tag: str, error_message_id: str + ): + """Checks to see if specific error message id appears in the security request error""" + commands = self.result["securityResult"][security_definition_tag].get( + "commands" + ) + if not isinstance(commands, list): + return False + messages = commands[0].get("messages", []) + if error_message_id in "".join(messages): + return True + else: + return False diff --git a/pyracf/common/security_response_error.py b/pyracf/common/security_response_error.py deleted file mode 100644 index f9701438..00000000 --- a/pyracf/common/security_response_error.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Exception to use when data returned by IRRSMO00 indicates that the request failed.""" - - -class SecurityResponseError(Exception): - """ - Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. - """ - - def __init__(self, result: dict) -> None: - self.message = "Security request made to IRRSMO00 failed." - self.result = result - self.message += ( - "\n\nSee results dictionary " - + f"'{self.__class__.__name__}.result' for more details." - ) - self.message = f"({self.__class__.__name__}) {self.message}" - - def __str__(self) -> str: - return self.message - - def contains_error_message( - self, security_definition_tag: str, error_message_id: str - ): - """Checks to see if specific error message id appears in the security request error""" - commands = self.result["securityResult"][security_definition_tag].get( - "commands" - ) - if not isinstance(commands, list): - return False - messages = commands[0].get("messages", []) - if error_message_id in "".join(messages): - return True - else: - return False diff --git a/pyracf/common/security_result.py b/pyracf/common/security_result.py index e371616d..6601ee97 100644 --- a/pyracf/common/security_result.py +++ b/pyracf/common/security_result.py @@ -1,7 +1,6 @@ """Generic Security Result Parser.""" import getpass -import platform import re from typing import Union from xml.etree.ElementTree import Element # Only used for type hints. @@ -25,7 +24,7 @@ def __init__( else: self.__result_dictionary["securityResult"][ "runningUserid" - ] = self.__get_current_userid() + ] = getpass.getuser().lower() def __extract_results(self) -> None: """Extract XML results into a dictionary.""" @@ -143,12 +142,6 @@ def __to_pascal_case(self, key: str) -> str: case _: return key - def __get_current_userid(self) -> str: - if platform.system() == "OS/390": - return getpass.getuser().lower() - else: - return "testuser" - def get_result_dictionary(self) -> dict: """Return result dictionary.""" return self.__result_dictionary diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index 83bbe02b..2b1df499 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_response_error import SecurityResponseError +from pyracf.common.security_request_error import SecurityRequestError from .data_set_request import DataSetRequest @@ -117,7 +117,7 @@ def add( ) if self._get_field(profile, "base", "name") == data_set.lower(): raise AddOperationError(data_set, self._profile_type) - except SecurityResponseError as exception: + except SecurityRequestError as exception: if not exception.contains_error_message(self._profile_type, "ICH35003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -142,7 +142,7 @@ def alter( profile = self.extract( data_set, volume=volume, generic=generic, profile_only=True ) - except SecurityResponseError: + except SecurityRequestError: raise AlterOperationError(data_set, self._profile_type) if not self._get_field(profile, "base", "name") == data_set.lower(): raise AlterOperationError(data_set, self._profile_type) diff --git a/pyracf/group/group_admin.py b/pyracf/group/group_admin.py index 4c057eef..bd78851d 100644 --- a/pyracf/group/group_admin.py +++ b/pyracf/group/group_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_response_error import SecurityResponseError +from pyracf.common.security_request_error import SecurityRequestError from .group_request import GroupRequest @@ -131,7 +131,7 @@ def add(self, group: str, traits: dict = {}) -> Union[dict, bytes]: return self._make_request(group_request) try: self.extract(group) - except SecurityResponseError as exception: + except SecurityRequestError as exception: if not exception.contains_error_message(self._profile_type, "ICH51003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -149,7 +149,7 @@ def alter(self, group: str, traits: dict) -> Union[dict, bytes]: return self._make_request(group_request, irrsmo00_precheck=True) try: self.extract(group) - except SecurityResponseError: + except SecurityRequestError: raise AlterOperationError(group, self._profile_type) self._build_segment_trait_dictionary(traits) group_request = GroupRequest(group, "set") diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index d749a8d5..87d6cd64 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_response_error import SecurityResponseError +from pyracf.common.security_request_error import SecurityRequestError from .resource_request import ResourceRequest @@ -510,7 +510,7 @@ def add( profile = self.extract(resource, class_name, profile_only=True) if self._get_field(profile, "base", "name") == resource.lower(): raise AddOperationError(resource, class_name) - except SecurityResponseError as exception: + except SecurityRequestError as exception: if not exception.contains_error_message(self._profile_type, "ICH13003I"): raise exception self._build_segment_trait_dictionary(traits) @@ -527,7 +527,7 @@ def alter(self, resource: str, class_name: str, traits: dict) -> Union[dict, byt return self._make_request(profile_request, irrsmo00_precheck=True) try: profile = self.extract(resource, class_name, profile_only=True) - except SecurityResponseError: + except SecurityRequestError: raise AlterOperationError(resource, class_name) if not self._get_field(profile, "base", "name") == resource.lower(): raise AlterOperationError(resource, class_name) diff --git a/pyracf/scripts/install_precheck.py b/pyracf/scripts/setup_precheck.py similarity index 92% rename from pyracf/scripts/install_precheck.py rename to pyracf/scripts/setup_precheck.py index 276a2937..f4e7c80f 100644 --- a/pyracf/scripts/install_precheck.py +++ b/pyracf/scripts/setup_precheck.py @@ -1,11 +1,11 @@ -from pyracf import ResourceAdmin, SecurityResponseError +from pyracf import ResourceAdmin, SecurityRequestError -def define_precheck_profile(): +def setup_precheck(): resource_admin = ResourceAdmin() try: access = resource_admin.get_my_access("IRR.IRRSMO00.PRECHECK", "XFACILIT") - except SecurityResponseError: + except SecurityRequestError: traits_precheck = {"base:universal_access": "None"} result = resource_admin.add( "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck diff --git a/pyracf/user/user_admin.py b/pyracf/user/user_admin.py index 44caf1b8..6d1deb9a 100644 --- a/pyracf/user/user_admin.py +++ b/pyracf/user/user_admin.py @@ -5,7 +5,7 @@ from pyracf.common.add_operation_error import AddOperationError from pyracf.common.alter_operation_error import AlterOperationError from pyracf.common.security_admin import SecurityAdmin -from pyracf.common.security_response_error import SecurityResponseError +from pyracf.common.security_request_error import SecurityRequestError from .user_request import UserRequest @@ -777,7 +777,7 @@ def add(self, userid: str, traits: dict = {}) -> Union[dict, bytes]: return self._make_request(user_request) try: self.extract(userid) - except SecurityResponseError as exception: + except SecurityRequestError as exception: if not exception.contains_error_message(self._profile_type, "ICH30001I"): raise exception self._build_segment_trait_dictionary(traits) @@ -795,7 +795,7 @@ def alter(self, userid: str, traits: dict) -> Union[dict, bytes]: return self._make_request(user_request, irrsmo00_precheck=True) try: self.extract(userid) - except SecurityResponseError: + except SecurityRequestError: raise AlterOperationError(userid, self._profile_type) self._build_segment_trait_dictionary(traits) user_request = UserRequest(userid, "set") diff --git a/tests/access/test_access_debug_logging.py b/tests/access/test_access_debug_logging.py index 6cb6d682..8f70e86b 100644 --- a/tests/access/test_access_debug_logging.py +++ b/tests/access/test_access_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.access.test_access_constants as TestAccessConstants -from pyracf import AccessAdmin, SecurityResponseError +from pyracf import AccessAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -56,7 +56,7 @@ def test_permit_access_request_debug_log_works_on_error( self.access_admin.permit( "TESTING", "ELIJTEST", "MCGINLEY", traits={"base:access": "ALTER"} ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestAccessConstants.TEST_PERMIT_ACCESS_ERROR_LOG) diff --git a/tests/access/test_access_result_parser.py b/tests/access/test_access_result_parser.py index 950a56e0..cbb94348 100644 --- a/tests/access/test_access_result_parser.py +++ b/tests/access/test_access_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.access.test_access_constants as TestAccessConstants -from pyracf import AccessAdmin, SecurityResponseError +from pyracf import AccessAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -44,7 +44,7 @@ def test_access_admin_can_parse_permit_access_error_xml( call_racf_mock.return_value = ( TestAccessConstants.TEST_PERMIT_ACCESS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.access_admin.permit( "TESTING", "ELIJTEST", "MCGINLEY", traits={"base:access": "ALTER"} ) @@ -69,14 +69,11 @@ def test_access_admin_can_parse_delete_access_success_xml( ) # Error User not authorized, delete ignored - def test_access_admin_can_parse_delete_access_error_xml( - self, - call_racf_mock: Mock, - ): + def test_access_admin_can_parse_delete_access_error_xml(self, call_racf_mock: Mock): call_racf_mock.return_value = ( TestAccessConstants.TEST_DELETE_ACCESS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.access_admin.delete("TESTING", "ELIJTEST", "ESWIFT") self.assertEqual( exception.exception.result, diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 981a6831..c3518a29 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -118,7 +118,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: # ============================================================================ TEST_NULL_RESPONSE_PRECHECK_TEXT = ( - "(SecurityRequestError) Security request made to IRRSMO00 failed." + "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For `set` or `alter` functions, you must have at least READ " @@ -126,7 +126,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) TEST_NULL_RESPONSE_SURROGAT_TEXT = ( - "(SecurityRequestError) Security request made to IRRSMO00 failed." + "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the `run_as_userid` feature, you must have at least UPDATE" @@ -134,7 +134,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) TEST_NULL_RESPONSE_GENERIC_TEXT = ( - "(SecurityRequestError) Security request made to IRRSMO00 failed." + "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against" + " the IRRSMO00 documented return and reason codes for more information" diff --git a/tests/common/test_install_precheck_script.py b/tests/common/test_install_precheck_script.py index 35efc2be..53426057 100644 --- a/tests/common/test_install_precheck_script.py +++ b/tests/common/test_install_precheck_script.py @@ -9,7 +9,7 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import define_precheck_profile +from pyracf import setup_precheck from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -33,7 +33,7 @@ def test_install_precheck_works_when_no_setup_done( ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_DEFINED_PROFILE_TEXT @@ -52,7 +52,7 @@ def test_install_precheck_works_when_alter_access_exists( ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( precheck_log, @@ -80,7 +80,7 @@ def test_install_precheck_works_when_control_access_exists( ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(precheck_log, validated_control_access) self.assertEqual(result, True) @@ -105,7 +105,7 @@ def test_install_precheck_works_when_read_access_exists( ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(precheck_log, validated_read_access) self.assertEqual(result, True) @@ -130,7 +130,7 @@ def test_install_precheck_works_when_update_access_exists( ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(precheck_log, validated_update_access) self.assertEqual(result, True) @@ -149,7 +149,7 @@ def test_install_precheck_works_when_none_access_exists( call_racf_mock.return_value = extract_with_none_access stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - result = define_precheck_profile() + result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_FOUND_NO_ACCESS_TEXT diff --git a/tests/common/test_security_request_error.py b/tests/common/test_security_request_error.py index 64ec1bd1..9bf3667f 100644 --- a/tests/common/test_security_request_error.py +++ b/tests/common/test_security_request_error.py @@ -6,7 +6,7 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import SecurityRequestError, UserAdmin +from pyracf import DownstreamFatalError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -14,7 +14,7 @@ @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") -class TestSecurityRequestError(unittest.TestCase): +class TestDownstreamFatalError(unittest.TestCase): maxDiff = None IRRSMO00.__init__ = Mock(return_value=None) user_admin = UserAdmin() @@ -24,7 +24,7 @@ def test_security_request_error_thrown_on_precheck_error( call_racf_mock: Mock, ): call_racf_mock.return_value = [8, 200, 16] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(DownstreamFatalError) as exception: self.user_admin.set_password("TESTUSER", "Testpass") self.assertEqual( exception.exception.message, @@ -37,7 +37,7 @@ def test_security_request_error_thrown_on_surrogat_error( ): call_racf_mock.return_value = [8, 200, 8] self.user_admin.set_running_userid("ESWIFT") - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(DownstreamFatalError) as exception: self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, @@ -49,7 +49,7 @@ def test_security_request_error_thrown_on_miscellaneous_error( call_racf_mock: Mock, ): call_racf_mock.return_value = [8, 2000, 20] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(DownstreamFatalError) as exception: self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, diff --git a/tests/connection/test_connection_debug_logging.py b/tests/connection/test_connection_debug_logging.py index fd0fbec5..7adabd20 100644 --- a/tests/connection/test_connection_debug_logging.py +++ b/tests/connection/test_connection_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.connection.test_connection_constants as TestConnectionConstants -from pyracf import ConnectionAdmin, SecurityResponseError +from pyracf import ConnectionAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_connect_connection_request_debug_log_works_on_error( "TESTGRP0", traits=TestConnectionConstants.TEST_CONNECT_CONNECTION_REQUEST_TRAITS, ), - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/connection/test_connection_result_parser.py b/tests/connection/test_connection_result_parser.py index b84752c5..dcd724a2 100644 --- a/tests/connection/test_connection_result_parser.py +++ b/tests/connection/test_connection_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.connection.test_connection_constants as TestConnectionConstants -from pyracf import ConnectionAdmin, SecurityResponseError +from pyracf import ConnectionAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -46,7 +46,7 @@ def test_connection_admin_can_parse_connect_connection_error_xml( call_racf_mock.return_value = ( TestConnectionConstants.TEST_CONNECT_CONNECTION_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.connection_admin.connect( "ESWIFT", "TESTGRP0", @@ -80,7 +80,7 @@ def test_connection_admin_can_parse_delete_connection_error_xml( call_racf_mock.return_value = ( TestConnectionConstants.TEST_DELETE_CONNECTION_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.connection_admin.delete("ESWIFT", "TESTGRP0") self.assertEqual( exception.exception.result, diff --git a/tests/data_set/test_data_set_debug_logging.py b/tests/data_set/test_data_set_debug_logging.py index def94a00..01a13452 100644 --- a/tests/data_set/test_data_set_debug_logging.py +++ b/tests/data_set/test_data_set_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.data_set.test_data_set_constants as TestDataSetConstants -from pyracf import DataSetAdmin, SecurityResponseError +from pyracf import DataSetAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_alter_data_set_request_debug_log_works_on_error( "ESWIFT.TEST.T1136242.P3020470", traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestDataSetConstants.TEST_ALTER_DATA_SET_ERROR_LOG) @@ -95,7 +95,7 @@ def test_extract_data_set_base_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/data_set/test_data_set_getters.py b/tests/data_set/test_data_set_getters.py index 494bb0bf..0437b283 100644 --- a/tests/data_set/test_data_set_getters.py +++ b/tests/data_set/test_data_set_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.data_set.test_data_set_constants as TestDataSetConstants -from pyracf import DataSetAdmin, SecurityResponseError +from pyracf import DataSetAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -60,7 +60,7 @@ def test_data_set_admin_get_universal_access_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.data_set_admin.get_universal_access("ESWIFT.TEST.T1136242.P3020470") def test_data_set_admin_get_my_access_returns_valid_when_alter( @@ -98,5 +98,5 @@ def test_data_set_admin_get_my_access_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.data_set_admin.get_my_access("ESWIFT.TEST.T1136242.P3020470") diff --git a/tests/data_set/test_data_set_result_parser.py b/tests/data_set/test_data_set_result_parser.py index 653cd449..c4417d35 100644 --- a/tests/data_set/test_data_set_result_parser.py +++ b/tests/data_set/test_data_set_result_parser.py @@ -10,7 +10,7 @@ AddOperationError, AlterOperationError, DataSetAdmin, - SecurityResponseError, + SecurityRequestError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -90,7 +90,7 @@ def test_data_set_admin_can_parse_add_data_set_error_xml( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BAD_ATTRIBUTE_ERROR_XML, TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.add( "ESWIFTTESTT1136242P3020470", traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, @@ -172,7 +172,7 @@ def test_data_set_admin_can_parse_alter_data_set_error_xml( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.alter( "ESWIFT.TEST.T1136242.P3020470", traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, @@ -217,7 +217,7 @@ def test_data_set_admin_can_parse_extract_data_set_base_only_error_xml( call_racf_mock.return_value = ( TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") self.assertEqual( exception.exception.result, @@ -247,7 +247,7 @@ def test_data_set_admin_can_parse_delete_data_set_error_xml( call_racf_mock.return_value = ( TestDataSetConstants.TEST_DELETE_DATA_SET_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.delete("ESWIFT.TEST.T1136242.P3020470") self.assertEqual( exception.exception.result, diff --git a/tests/group/test_group_debug_logging.py b/tests/group/test_group_debug_logging.py index 0d33c8d1..d4d63265 100644 --- a/tests/group/test_group_debug_logging.py +++ b/tests/group/test_group_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.group.test_group_constants as TestGroupConstants -from pyracf import GroupAdmin, SecurityResponseError +from pyracf import GroupAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -57,7 +57,7 @@ def test_alter_group_request_debug_log_works_on_error( "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestGroupConstants.TEST_ALTER_GROUP_ERROR_LOG) @@ -91,7 +91,7 @@ def test_extract_group_base_omvs_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.group_admin.extract("TESTGRP0", segments=["omvs"]) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/group/test_group_getters.py b/tests/group/test_group_getters.py index 9805fa2a..25177201 100644 --- a/tests/group/test_group_getters.py +++ b/tests/group/test_group_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.group.test_group_constants as TestGroupConstants -from pyracf import GroupAdmin, SecurityResponseError +from pyracf import GroupAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -52,7 +52,7 @@ def test_group_admin_has_group_special_authority_raises_an_exception_when_extrac call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.has_group_special_authority("TESTGRP0", "ESWIFT") # ============================================================================ @@ -88,7 +88,7 @@ def test_group_admin_has_group_operations_authority_raises_an_exception_when_ext call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.has_group_operations_authority("TESTGRP0", "LEONARD") # ============================================================================ @@ -129,7 +129,7 @@ def test_group_admin_has_group_auditor_authority_raises_an_exception_when_extrac call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.has_group_auditor_authority("TESTGRP0", "ESWIFT") # ============================================================================ @@ -170,7 +170,7 @@ def test_group_admin_has_group_access_attribute_raises_an_exception_when_extract call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.has_group_access_attribute("TESTGRP0", "LEONARD") # ============================================================================ @@ -193,7 +193,7 @@ def test_group_admin_get_omvs_gid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.get_omvs_gid("TESTGRP0"), 1234567 def test_group_admin_get_omvs_gid_returns_none_when_no_omvs_segment_exists( @@ -229,7 +229,7 @@ def test_group_admin_get_ovm_gid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.group_admin.get_ovm_gid("TESTGRP0"), 1234567 def test_group_admin_get_ovm_gid_returns_none_when_no_ovm_segment_exists( diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index 772a9c59..69054faa 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -9,9 +9,9 @@ from pyracf import ( AddOperationError, AlterOperationError, + DownstreamFatalError, GroupAdmin, SecurityRequestError, - SecurityResponseError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -72,7 +72,7 @@ def test_group_admin_can_parse_add_group_error_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BAD_ATTRIBUTE_ERROR_XML, TestGroupConstants.TEST_ADD_GROUP_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(DownstreamFatalError) as exception: self.group_admin.add( "TESTGRPP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ) @@ -128,7 +128,7 @@ def test_group_admin_can_parse_alter_group_error_xml( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.group_admin.alter( "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, @@ -173,7 +173,7 @@ def test_group_admin_can_parse_extract_group_base_omvs_error_xml( call_racf_mock.return_value = ( TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.group_admin.extract("TESTGRP0", segments=["omvs"]) self.assertEqual( exception.exception.result, @@ -203,7 +203,7 @@ def test_group_admin_can_parse_delete_group_error_xml( call_racf_mock.return_value = ( TestGroupConstants.TEST_DELETE_GROUP_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.group_admin.delete("TESTGRP0") self.assertEqual( exception.exception.result, diff --git a/tests/resource/test_resource_debug_logging.py b/tests/resource/test_resource_debug_logging.py index 9a83ad3f..8342649d 100644 --- a/tests/resource/test_resource_debug_logging.py +++ b/tests/resource/test_resource_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityResponseError +from pyracf import ResourceAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -62,7 +62,7 @@ def test_alter_resource_request_debug_log_works_on_error( "ELIJTEST", traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestResourceConstants.TEST_ALTER_RESOURCE_ERROR_LOG) @@ -96,7 +96,7 @@ def test_extract_resource_base_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.resource_admin.extract("TESTING", "ELIJTEST") - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/resource/test_resource_getters.py b/tests/resource/test_resource_getters.py index b6ffc315..4f321ed1 100644 --- a/tests/resource/test_resource_getters.py +++ b/tests/resource/test_resource_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityResponseError +from pyracf import ResourceAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -57,7 +57,7 @@ def test_resource_admin_get_universal_access_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.get_universal_access("TESTING", "ELIJTEST") def test_resource_admin_get_my_access_returns_valid_when_read( @@ -93,5 +93,5 @@ def test_resource_admin_get_my_access_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.get_my_access("TESTING", "ELIJTEST") diff --git a/tests/resource/test_resource_result_parser.py b/tests/resource/test_resource_result_parser.py index e59f1377..525366ee 100644 --- a/tests/resource/test_resource_result_parser.py +++ b/tests/resource/test_resource_result_parser.py @@ -10,7 +10,7 @@ AddOperationError, AlterOperationError, ResourceAdmin, - SecurityResponseError, + SecurityRequestError, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -85,7 +85,7 @@ def test_resource_admin_can_parse_add_resource_error_xml( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BAD_CLASS_ERROR_XML, TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.resource_admin.add("TESTING", "ELIXTEST") self.assertEqual( exception.exception.result, @@ -167,7 +167,7 @@ def test_resource_admin_can_parse_alter_resource_error_xml( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.resource_admin.alter( "TESTING", "ELIJTEST", @@ -214,7 +214,7 @@ def test_resource_admin_can_parse_extract_resource_base_error_xml( call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.resource_admin.extract("TESTING", "ELIJTEST") self.assertEqual( exception.exception.result, @@ -244,7 +244,7 @@ def test_resource_admin_can_parse_delete_resource_error_xml( call_racf_mock.return_value = ( TestResourceConstants.TEST_DELETE_RESOURCE_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.resource_admin.delete("TESTING", "ELIJTEST") self.assertEqual( exception.exception.result, diff --git a/tests/resource/test_resource_subfunction_extracts.py b/tests/resource/test_resource_subfunction_extracts.py index b7eac0f0..2f4787aa 100644 --- a/tests/resource/test_resource_subfunction_extracts.py +++ b/tests/resource/test_resource_subfunction_extracts.py @@ -6,7 +6,7 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import ResourceAdmin, SecurityResponseError +from pyracf import ResourceAdmin, SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -41,7 +41,7 @@ def test_resource_admin_extract_resource_class_raises_exception_when_extract_fai call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_CDTINFO_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_resource_class("SHELCITY") # ============================================================================ @@ -66,7 +66,7 @@ def test_resource_admin_build_extract_started_task_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_STDATA_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_started_task("TSTTSKEL") # ============================================================================ @@ -91,7 +91,7 @@ def test_resource_admin_build_extract_custom_field_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_CFDEF_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_custom_field("TVSHOW", "user") # ============================================================================ @@ -116,7 +116,7 @@ def test_resource_admin_build_extract_kerberos_realm_raises_exception_when_extra call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_KERB_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_kerberos_realm("TSTREALM") # ============================================================================ @@ -141,7 +141,7 @@ def test_resource_admin_build_extract_signed_program_raises_exception_when_extra call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SIGVER_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_signed_program("TESTPRGM") # ============================================================================ @@ -166,5 +166,5 @@ def test_resource_admin_build_extract_appc_session_raises_exception_when_extract call_racf_mock.return_value = ( TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SESSION_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.resource_admin.extract_appc_session("TSTNET", "TSTLOCLU", "TSTPRTLU") diff --git a/tests/setropts/test_setropts_debug_logging.py b/tests/setropts/test_setropts_debug_logging.py index 2af953e2..b6876dd2 100644 --- a/tests/setropts/test_setropts_debug_logging.py +++ b/tests/setropts/test_setropts_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityResponseError, SetroptsAdmin +from pyracf import SecurityRequestError, SetroptsAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -52,7 +52,7 @@ def test_alter_setropts_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.setropts_admin.alter(options={"base:raclist": "ELIXTEST"}) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestSetroptsConstants.TEST_ALTER_SETROPTS_ERROR_LOG) diff --git a/tests/setropts/test_setropts_getters.py b/tests/setropts/test_setropts_getters.py index 7a39ca9b..2f52675d 100644 --- a/tests/setropts/test_setropts_getters.py +++ b/tests/setropts/test_setropts_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityResponseError +from pyracf import SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 from pyracf.setropts.setropts_admin import SetroptsAdmin @@ -43,7 +43,7 @@ def test_setropts_admin_get_password_rules_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.setropts_admin.get_password_rules() # ============================================================================ @@ -70,5 +70,5 @@ def test_setropts_admin_get_class_attributes_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.setropts_admin.get_class_attributes("FACILITY") diff --git a/tests/setropts/test_setropts_result_parser.py b/tests/setropts/test_setropts_result_parser.py index 790d2c15..3ebfe7bb 100644 --- a/tests/setropts/test_setropts_result_parser.py +++ b/tests/setropts/test_setropts_result_parser.py @@ -6,7 +6,7 @@ import __init__ import tests.setropts.test_setropts_constants as TestSetroptsConstants -from pyracf import SecurityResponseError +from pyracf import SecurityRequestError from pyracf.common.irrsmo00 import IRRSMO00 from pyracf.setropts.setropts_admin import SetroptsAdmin @@ -43,7 +43,7 @@ def test_setropts_admin_can_parse_add_setropts_error_xml( call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.setropts_admin.alter(options={"base:raclist": "elixtest"}) self.assertEqual( exception.exception.result, @@ -97,7 +97,7 @@ def test_setropts_admin_can_parse_list_setropts_error_xml( call_racf_mock.return_value = ( TestSetroptsConstants.TEST_ALTER_SETROPTS_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: # Not valid error for this request, but good test self.setropts_admin.list_racf_options() self.assertEqual( diff --git a/tests/test_runner.py b/tests/test_runner.py index e46b18a2..946b33b0 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -1,7 +1,9 @@ """Used to drive all unit tests for pyRACF.""" +import getpass import sys import unittest +from unittest.mock import Mock import __init__ @@ -12,7 +14,7 @@ from tests.common.test_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger from tests.common.test_run_as_userid import TestRunAsUserId -from tests.common.test_security_request_error import TestSecurityRequestError +from tests.common.test_security_request_error import TestDownstreamFatalError from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -57,6 +59,7 @@ def __test_suite() -> unittest.TestSuite: """Load and run each unit test class.""" + getpass.getuser = Mock(return_value="testuser") test_suite = unittest.TestSuite() test_loader = unittest.TestLoader() test_classes = [ @@ -66,7 +69,7 @@ def __test_suite() -> unittest.TestSuite: TestCustomizeSegmentTraits, TestInstallPrecheckScript, TestLogger, - TestSecurityRequestError, + TestDownstreamFatalError, TestRunAsUserId, TestConnectionResultParser, TestConnectionRequestBuilder, diff --git a/tests/user/test_user_debug_logging.py b/tests/user/test_user_debug_logging.py index 3a3e157e..c3b27d08 100644 --- a/tests/user/test_user_debug_logging.py +++ b/tests/user/test_user_debug_logging.py @@ -9,7 +9,7 @@ import __init__ import tests.user.test_user_constants as TestUserConstants -from pyracf import SecurityResponseError, UserAdmin +from pyracf import SecurityRequestError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -61,7 +61,7 @@ def test_alter_user_request_debug_log_works_on_error( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual(error_log, TestUserConstants.TEST_ALTER_USER_ERROR_LOG) @@ -108,7 +108,7 @@ def test_alter_user_request_debug_log_passwords_get_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -155,7 +155,7 @@ def test_alter_user_request_debug_log_passphrases_get_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -204,7 +204,7 @@ def test_alter_user_request_debug_log_passphrases_and_passwords_get_redacted_on_ "squidwrd", traits=error_traits, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -254,7 +254,7 @@ def test_alter_user_request_debug_log_password_xml_tags_not_redacted_on_error( "squidwrd", traits=error_traits, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -309,7 +309,7 @@ def test_alter_user_request_debug_log_additional_secret_added_get_redacted_on_er "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, ) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( @@ -350,7 +350,7 @@ def test_extract_user_base_omvs_request_debug_log_works_on_error( with contextlib.redirect_stdout(stdout): try: self.user_admin.extract("squidwrd", segments=["omvs"]) - except SecurityResponseError: + except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( diff --git a/tests/user/test_user_getters.py b/tests/user/test_user_getters.py index a4dc4086..f184cb11 100644 --- a/tests/user/test_user_getters.py +++ b/tests/user/test_user_getters.py @@ -6,7 +6,7 @@ import __init__ import tests.user.test_user_constants as TestUserConstants -from pyracf import SecurityResponseError, UserAdmin +from pyracf import SecurityRequestError, UserAdmin from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -53,7 +53,7 @@ def test_user_admin_has_special_authority_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.has_special_authority("squidwrd") # ============================================================================ @@ -91,7 +91,7 @@ def test_user_admin_has_auditory_authority_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.has_auditor_authority("squidwrd") # ============================================================================ @@ -128,7 +128,7 @@ def test_user_admin_has_operations_authority_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.has_operations_authority("squidwrd") # ============================================================================ @@ -162,7 +162,7 @@ def test_user_admin_get_class_authorizations_raises_an_exception_when_extract_fa call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_class_authorizations("squidwrd") # ============================================================================ @@ -268,7 +268,7 @@ def test_user_admin_get_omvs_uid_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_uid("squidwrd") def test_user_admin_get_omvs_uid_returns_none_when_no_omvs_segment_exists( @@ -301,7 +301,7 @@ def test_user_admin_get_omvs_max_address_space_size_raises_an_exception_when_ext call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_address_space_size("squidwrd") def test_user_admin_get_omvs_max_address_space_size_returns_none_when_no_omvs_segment_exists( @@ -332,7 +332,7 @@ def test_user_admin_get_omvs_max_cpu_time_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_cpu_time("squidwrd") def test_user_admin_get_omvs_max_cpu_time_returns_none_when_no_omvs_segment_exists( @@ -363,7 +363,7 @@ def test_user_admin_get_omvs_max_files_per_process_raises_an_exception_when_extr call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_files_per_process("squidwrd") def test_user_admin_get_omvs_max_files_per_process_returns_none_when_no_omvs_segment_exists( @@ -396,7 +396,7 @@ def test_user_admin_get_omvs_max_non_shared_memory_raises_an_exception_when_extr call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_non_shared_memory("squidwrd") def test_user_admin_get_omvs_max_non_shared_memory_returns_none_when_field_is_unset( @@ -438,7 +438,7 @@ def test_user_admin_get_omvs_max_file_mapping_pages_raises_an_exception_when_ext call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_file_mapping_pages("squidwrd") def test_user_admin_get_omvs_max_file_mapping_pages_returns_none_when_no_omvs_segment_exists( @@ -469,7 +469,7 @@ def test_user_admin_get_omvs_max_processes_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_processes("squidwrd") def test_user_admin_get_omvs_max_processes_returns_none_when_no_omvs_segment_exists( @@ -500,7 +500,7 @@ def test_user_admin_get_omvs_max_shared_memory_raises_an_exception_when_extract_ call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_shared_memory("squidwrd") def test_user_admin_get_omvs_max_shared_memory_returns_none_when_field_is_unset( @@ -540,7 +540,7 @@ def test_user_admin_get_omvs_max_threads_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_max_threads("squidwrd") def test_user_admin_get_omvs_max_threads_returns_none_when_no_omvs_segment_exists( @@ -573,7 +573,7 @@ def test_user_admin_get_omvs_home_directory_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_home_directory("squidwrd") def test_user_admin_get_omvs_home_directory_returns_none_when_no_omvs_segment_exists( @@ -626,7 +626,7 @@ def test_user_admin_get_omvs_default_shell_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_omvs_default_shell("squidwrd") def test_user_admin_get_omvs_default_shell_returns_none_when_no_omvs_segment_exists( @@ -675,7 +675,7 @@ def test_user_admin_get_tso_account_number_raises_an_exception_when_extract_fail call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_account_number("squidwrd") def test_user_admin_get_tso_account_number_returns_none_when_no_tso_segment_exists( @@ -715,7 +715,7 @@ def test_user_admin_get_tso_logon_command_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_logon_command("squidwrd") def test_user_admin_get_tso_logon_command_returns_none_when_no_tso_segment_exists( @@ -755,7 +755,7 @@ def test_user_admin_get_tso_hold_class_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_hold_class("squidwrd") def test_user_admin_get_tso_hold_class_returns_none_when_no_tso_segment_exists( @@ -795,7 +795,7 @@ def test_user_admin_get_tso_max_region_size_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_max_region_size("squidwrd") def test_user_admin_get_tso_max_region_size_returns_none_when_no_tso_segment_exists( @@ -826,7 +826,7 @@ def test_user_admin_get_tso_message_class_raises_an_exception_when_extract_fails call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_message_class("squidwrd") def test_user_admin_get_tso_message_class_returns_none_when_no_tso_segment_exists( @@ -866,7 +866,7 @@ def test_user_admin_get_tso_logon_procedure_raises_an_exception_when_extract_fai call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_logon_procedure("squidwrd") def test_user_admin_get_tso_logon_procedure_returns_none_when_no_tso_segment_exists( @@ -906,7 +906,7 @@ def test_user_admin_get_tso_default_region_size_raises_an_exception_when_extract call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_default_region_size("squidwrd") def test_user_admin_get_default_tso_region_size_returns_none_when_no_tso_segment_exists( @@ -937,7 +937,7 @@ def test_user_admin_get_tso_sysout_class_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_sysout_class("squidwrd") def test_user_admin_get_tso_sysout_class_returns_none_when_no_tso_segment_exists( @@ -977,7 +977,7 @@ def test_user_admin_get_tso_user_data_raises_an_exception_when_extract_fails( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_user_data("squidwrd") def test_user_admin_get_tso_user_data_returns_none_when_no_tso_segment_exists( @@ -1019,7 +1019,7 @@ def test_user_admin_get_tso_data_set_allocation_unit_raises_an_exception_when_ex call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError): + with self.assertRaises(SecurityRequestError): self.user_admin.get_tso_data_set_allocation_unit("squidwrd") def test_user_admin_get_tso_data_set_allocation_unit_returns_none_when_no_tso_segment_exists( diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index e6cd8ae6..a4b483ca 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -9,8 +9,8 @@ from pyracf import ( AddOperationError, AlterOperationError, + DownstreamFatalError, SecurityRequestError, - SecurityResponseError, UserAdmin, ) from pyracf.common.irrsmo00 import IRRSMO00 @@ -76,7 +76,7 @@ def test_user_admin_can_parse_add_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BAD_ATTRIBUTE_XML, TestUserConstants.TEST_ADD_USER_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityRequestError) as exception: + with self.assertRaises(DownstreamFatalError) as exception: self.user_admin.add( "squidward", traits=TestUserConstants.TEST_ADD_USER_REQUEST_TRAITS, @@ -133,7 +133,7 @@ def test_user_admin_can_parse_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.alter( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, @@ -178,7 +178,7 @@ def test_user_admin_can_parse_extract_user_base_omvs_error_xml( call_racf_mock.return_value = ( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_OMVS_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.extract("squidwrd", segments=["omvs"]) self.assertEqual( exception.exception.result, @@ -253,7 +253,7 @@ def test_user_admin_password_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSWORD ) @@ -299,7 +299,7 @@ def test_user_admin_passphrase_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSPHRASE_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSPHRASE ) @@ -347,7 +347,7 @@ def test_user_admin_passphrase_and_password_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSPHRASE_AND_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSPHRASE_AND_PASSWORD ) @@ -396,7 +396,7 @@ def test_user_admin_password_message_not_redacted_alter_user_error_xml( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_PASSWORD_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: error_traits = dict( TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_PASSWORD_SIMPLE ) @@ -437,7 +437,7 @@ def test_user_admin_can_parse_delete_user_error_xml( call_racf_mock.return_value = ( TestUserConstants.TEST_DELETE_USER_RESULT_ERROR_XML ) - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: self.user_admin.delete("squidwrd") self.assertEqual( exception.exception.result, @@ -480,7 +480,7 @@ def test_user_admin_custom_secret_redacted_on_error( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestUserConstants.TEST_ALTER_USER_RESULT_ERROR_XML, ] - with self.assertRaises(SecurityResponseError) as exception: + with self.assertRaises(SecurityRequestError) as exception: user_admin.alter( "squidwrd", traits=TestUserConstants.TEST_ALTER_USER_REQUEST_TRAITS_UID_ERROR, From 04c028da264d17a21b74f1d976cbb98dbdde973b Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Dec 2023 13:54:04 -0500 Subject: [PATCH 15/30] Naming and Docstring Updates Signed-off-by: Elijah Swift --- pyracf/common/downstream_fatal_error.py | 4 +-- tests/common/test_common_constants.py | 6 ++--- ...check_script.py => test_setup_precheck.py} | 26 +++++++++---------- tests/test_runner.py | 4 +-- 4 files changed, 20 insertions(+), 20 deletions(-) rename tests/common/{test_install_precheck_script.py => test_setup_precheck.py} (85%) diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index c3882837..33c5b4ca 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -1,10 +1,10 @@ -"""Exception to use when no data is returned by IRRSMO00.""" +"""Exception to use when IRRSMO00 returns with an error.""" from typing import Union class DownstreamFatalError(Exception): """ - Raised when no xml string is returned by IRRSMO00. + Raised IRRSMO00 returns with a return code of 8, indicating an error occured prior to request. """ def __init__( diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index c3518a29..8a36a443 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -29,20 +29,20 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: "add_resource_result_precheck_uacc_none_success.json", "resource" ) -TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT = ( +TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( "IRR.IRRSMO00.PRECHECK is already defined, and you already have alter access!" + "\nYou are ready to start using pyRACF!" + "\nPlease ensure other users of pyRACF also have at least read access." + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) -TEST_INSTALL_PRECHECK_FOUND_NO_ACCESS_TEXT = ( +TEST_SETUP_PRECHECK_FOUND_NO_ACCESS_TEXT = ( "IRR.IRRSMO00.PRECHECK is already defined, but you have no access." + "\nContact your security administrator for READ access before using pyRACF." + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) -TEST_INSTALL_PRECHECK_DEFINED_PROFILE_TEXT = ( +TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT = ( "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None." + "\nContact your security administrator for READ access before using pyRACF." + "\nOther users of pyRACF will also need to have at least read access." diff --git a/tests/common/test_install_precheck_script.py b/tests/common/test_setup_precheck.py similarity index 85% rename from tests/common/test_install_precheck_script.py rename to tests/common/test_setup_precheck.py index 53426057..7af95173 100644 --- a/tests/common/test_install_precheck_script.py +++ b/tests/common/test_setup_precheck.py @@ -17,12 +17,12 @@ @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") -class TestInstallPrecheckScript(unittest.TestCase): +class TestSetupPrecheck(unittest.TestCase): maxDiff = None IRRSMO00.__init__ = Mock(return_value=None) ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") - def test_install_precheck_works_when_no_setup_done( + def test_setup_precheck_works_when_no_setup_done( self, call_racf_mock: Mock, ): @@ -36,14 +36,14 @@ def test_install_precheck_works_when_no_setup_done( result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_DEFINED_PROFILE_TEXT + precheck_log, TestCommonConstants.TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT ) self.assertEqual( result, TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY, ) - def test_install_precheck_works_when_alter_access_exists( + def test_setup_precheck_works_when_alter_access_exists( self, call_racf_mock: Mock, ): @@ -56,11 +56,11 @@ def test_install_precheck_works_when_alter_access_exists( precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( precheck_log, - TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT, + TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT, ) self.assertEqual(result, True) - def test_install_precheck_works_when_control_access_exists( + def test_setup_precheck_works_when_control_access_exists( self, call_racf_mock: Mock, ): @@ -73,7 +73,7 @@ def test_install_precheck_works_when_control_access_exists( ) call_racf_mock.return_value = extract_with_control_access validated_control_access = ( - TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_control_access = validated_control_access.replace( "you already have alter access!", "you already have control access!" @@ -85,7 +85,7 @@ def test_install_precheck_works_when_control_access_exists( self.assertEqual(precheck_log, validated_control_access) self.assertEqual(result, True) - def test_install_precheck_works_when_read_access_exists( + def test_setup_precheck_works_when_read_access_exists( self, call_racf_mock: Mock, ): @@ -98,7 +98,7 @@ def test_install_precheck_works_when_read_access_exists( ) call_racf_mock.return_value = extract_with_read_access validated_read_access = ( - TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_read_access = validated_read_access.replace( "you already have alter access!", "you already have read access!" @@ -110,7 +110,7 @@ def test_install_precheck_works_when_read_access_exists( self.assertEqual(precheck_log, validated_read_access) self.assertEqual(result, True) - def test_install_precheck_works_when_update_access_exists( + def test_setup_precheck_works_when_update_access_exists( self, call_racf_mock: Mock, ): @@ -123,7 +123,7 @@ def test_install_precheck_works_when_update_access_exists( ) call_racf_mock.return_value = extract_with_update_access validated_update_access = ( - TestCommonConstants.TEST_INSTALL_PRECHECK_VALIDATED_ACCESS_TEXT + TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_update_access = validated_update_access.replace( "you already have alter access!", "you already have update access!" @@ -135,7 +135,7 @@ def test_install_precheck_works_when_update_access_exists( self.assertEqual(precheck_log, validated_update_access) self.assertEqual(result, True) - def test_install_precheck_works_when_none_access_exists( + def test_setup_precheck_works_when_none_access_exists( self, call_racf_mock: Mock, ): @@ -152,6 +152,6 @@ def test_install_precheck_works_when_none_access_exists( result = setup_precheck() precheck_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - precheck_log, TestCommonConstants.TEST_INSTALL_PRECHECK_FOUND_NO_ACCESS_TEXT + precheck_log, TestCommonConstants.TEST_SETUP_PRECHECK_FOUND_NO_ACCESS_TEXT ) self.assertEqual(result, False) diff --git a/tests/test_runner.py b/tests/test_runner.py index 946b33b0..11daa833 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -11,10 +11,10 @@ from tests.access.test_access_request_builder import TestAccessRequestBuilder from tests.access.test_access_result_parser import TestAccessResultParser from tests.common.test_customize_segment_traits import TestCustomizeSegmentTraits -from tests.common.test_install_precheck_script import TestInstallPrecheckScript from tests.common.test_logger import TestLogger from tests.common.test_run_as_userid import TestRunAsUserId from tests.common.test_security_request_error import TestDownstreamFatalError +from tests.common.test_setup_precheck import TestSetupPrecheck from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( TestConnectionRequestBuilder, @@ -67,7 +67,7 @@ def __test_suite() -> unittest.TestSuite: TestAccessRequestBuilder, TestAccessDebugLogging, TestCustomizeSegmentTraits, - TestInstallPrecheckScript, + TestSetupPrecheck, TestLogger, TestDownstreamFatalError, TestRunAsUserId, From 46be4b89dba448557e84b89135b4da377fc25662 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 4 Dec 2023 16:36:57 -0500 Subject: [PATCH 16/30] Naming and convention fixes Signed-off-by: Elijah Swift --- pyracf/common/downstream_fatal_error.py | 37 ++++++++-------- pyracf/common/security_admin.py | 18 ++++---- pyracf/common/security_request_error.py | 4 +- pyracf/scripts/setup_precheck.py | 24 +++++----- tests/common/test_common_constants.py | 44 +++++++++---------- ...rror.py => test_downstream_fatal_error.py} | 8 ++-- tests/test_runner.py | 2 +- 7 files changed, 70 insertions(+), 67 deletions(-) rename tests/common/{test_security_request_error.py => test_downstream_fatal_error.py} (86%) diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index 33c5b4ca..48c81ed2 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -1,38 +1,39 @@ -"""Exception to use when IRRSMO00 returns with an error.""" -from typing import Union +"""Exception to use when IRRSMO00 is unable to process a request.""" +from typing import List, Union class DownstreamFatalError(Exception): """ - Raised IRRSMO00 returns with a return code of 8, indicating an error occured prior to request. + Raised IRRSMO00 returns with a SAF Return Code of 8, + indicating that the request could not be processed. """ def __init__( self, - xml_str: str, + return_reason_codes: List[int], request_xml: bytes, run_as_userid: Union[str, None] = None, - result_dict: dict = None, + result_dictionary: dict = None, ) -> None: self.message = "Security request made to IRRSMO00 failed." - self.saf_return_code = xml_str[0] - self.racf_return_code = xml_str[1] - self.racf_reason_code = xml_str[2] + self.saf_return_code = return_reason_codes[0] + self.racf_return_code = return_reason_codes[1] + self.racf_reason_code = return_reason_codes[2] self.request_xml = request_xml.decode("utf-8") self.message += ( f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" + f" {self.racf_return_code}\nRACF Reason Code: {self.racf_reason_code}" ) - if result_dict is not None: + if result_dictionary is not None: self.message += ( "\n\nSee results dictionary " + f"'{self.__class__.__name__}.result' for more details.\n" - + "\n\nYou can also check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" + + "\n\nYou can also check the specified return and reason codes against " + + "the IRRSMO00 documented return and reason codes for more information " + + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) - self.result = result_dict + self.result = result_dictionary elif ( (self.saf_return_code == 8) and (self.racf_return_code == 200) @@ -50,14 +51,14 @@ def __init__( ): self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE" - + f" access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." + + "For the `run_as_userid` feature, you must have at least UPDATE " + + f"access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." ) else: self.message += ( - "\n\nPlease check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" + "\n\nPlease check the specified return and reason codes against " + + "the IRRSMO00 documented return and reason codes for more information " + + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) self.message = f"({self.__class__.__name__}) {self.message}" diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 3d1b957a..f78fffd3 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -88,13 +88,13 @@ def __init__( # ============================================================================ # Run as Other User ID # ============================================================================ - def set_running_userid(self, new_userid: Union[str, None]) -> None: - if new_userid is None: - self.__running_userid = new_userid + def set_running_userid(self, userid: Union[str, None]) -> None: + if userid is None: + self.__running_userid = userid return - if not isinstance(new_userid, str) or len(new_userid) > 8 or new_userid == "": - raise UserIdError(new_userid) - self.__running_userid = new_userid.upper() + if not isinstance(userid, str) or len(userid) > 8 or userid == "": + raise UserIdError(userid) + self.__running_userid = userid.upper() def clear_running_userid(self) -> None: self.__running_userid = None @@ -197,10 +197,12 @@ def _make_request( self.__clear_state(security_request) if isinstance(result_xml, list): # When IRRSMO00 encounters some errors, it returns no XML response string. - # When this happens, the c code instead surfaces the return and reason + # When this happens, the C code instead surfaces the return and reason # codes which causes a DownstreamFatalError to be raised. raise DownstreamFatalError( - result_xml, request_xml, self.get_running_userid() + return_reason_codes=result_xml, + request_xml=request_xml, + run_as_userid=self.get_running_userid(), ) if self.__debug: # No need to redact anything here since the raw result XML diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 56d30d48..13a77e73 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -1,9 +1,9 @@ -"""Exception to use when data returned by IRRSMO00 indicates that the request failed.""" +"""Exception to use when IRRSMO00 processes a request with RACF warnings or errors.""" class SecurityRequestError(Exception): """ - Raised when the return code of a security result returned by IRRSMO00 is NOT equal to 0. + Raised when the SAF Return Code of a security result returned by IRRSMO00 is equal to 4. """ def __init__(self, result: dict) -> None: diff --git a/pyracf/scripts/setup_precheck.py b/pyracf/scripts/setup_precheck.py index f4e7c80f..95c26d87 100644 --- a/pyracf/scripts/setup_precheck.py +++ b/pyracf/scripts/setup_precheck.py @@ -11,24 +11,24 @@ def setup_precheck(): "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck ) print( - "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None." - + "\nContact your security administrator for READ access before using pyRACF." - + "\nOther users of pyRACF will also need to have at least read access." - + "\nYou may also need to REFRESH the `XFACILIT` class." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None.\n" + + "Contact your security administrator for READ access before using pyRACF.\n" + + "Other users of pyRACF will also need to have at least read access.\n" + + "You may also need to REFRESH the `XFACILIT` class.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return result if access: print( - f"IRR.IRRSMO00.PRECHECK is already defined, and you already have {access} access!" - + "\nYou are ready to start using pyRACF!" - + "\nPlease ensure other users of pyRACF also have at least read access." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + f"IRR.IRRSMO00.PRECHECK is already defined, and you already have {access} access!\n" + + "You are ready to start using pyRACF!\n" + + "Please ensure other users of pyRACF also have at least read access.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return True print( - "IRR.IRRSMO00.PRECHECK is already defined, but you have no access." - + "\nContact your security administrator for READ access before using pyRACF." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!" + "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" + + "Contact your security administrator for READ access before using pyRACF.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return False diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 8a36a443..2e9042e9 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -12,7 +12,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: # ============================================================================ -# Install Precheck Sample Data +# Setup Precheck Sample Data # ============================================================================ TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML = get_sample( @@ -30,24 +30,24 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, and you already have alter access!" - + "\nYou are ready to start using pyRACF!" - + "\nPlease ensure other users of pyRACF also have at least read access." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" + "IRR.IRRSMO00.PRECHECK is already defined, and you already have alter access!\n" + + "You are ready to start using pyRACF!\n" + + "Please ensure other users of pyRACF also have at least read access.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) TEST_SETUP_PRECHECK_FOUND_NO_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, but you have no access." - + "\nContact your security administrator for READ access before using pyRACF." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" + "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" + + "Contact your security administrator for READ access before using pyRACF.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT = ( - "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None." - + "\nContact your security administrator for READ access before using pyRACF." - + "\nOther users of pyRACF will also need to have at least read access." - + "\nYou may also need to REFRESH the `XFACILIT` class." - + "\nReview our documentation at https://ambitus.github.io/pyracf/ as well!\n" + "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None.\n" + + "Contact your security administrator for READ access before using pyRACF.\n" + + "Other users of pyRACF will also need to have at least read access.\n" + + "You may also need to REFRESH the `XFACILIT` class.\n" + + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) # ============================================================================ @@ -114,10 +114,10 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: ) # ============================================================================ -# Null Response Error +# Downstream Fatal Error # ============================================================================ -TEST_NULL_RESPONSE_PRECHECK_TEXT = ( +TEST_DOWNSTREAM_FATAL_PRECHECK_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" @@ -125,19 +125,19 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." ) -TEST_NULL_RESPONSE_SURROGAT_TEXT = ( +TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE" - + " access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." + + "For the `run_as_userid` feature, you must have at least UPDATE " + + "access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." ) -TEST_NULL_RESPONSE_GENERIC_TEXT = ( +TEST_DOWNSTREAM_FATAL_GENERIC_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" - + "\n\nPlease check the specified return and reason codes against" - + " the IRRSMO00 documented return and reason codes for more information" - + " about this error.\n" + + "\n\nPlease check the specified return and reason codes against " + + "the IRRSMO00 documented return and reason codes for more information " + + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) diff --git a/tests/common/test_security_request_error.py b/tests/common/test_downstream_fatal_error.py similarity index 86% rename from tests/common/test_security_request_error.py rename to tests/common/test_downstream_fatal_error.py index 9bf3667f..055ae89c 100644 --- a/tests/common/test_security_request_error.py +++ b/tests/common/test_downstream_fatal_error.py @@ -1,4 +1,4 @@ -"""Test null response error raising.""" +"""Test downstream fatal error raising.""" import unittest from unittest.mock import Mock, patch @@ -28,7 +28,7 @@ def test_security_request_error_thrown_on_precheck_error( self.user_admin.set_password("TESTUSER", "Testpass") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_NULL_RESPONSE_PRECHECK_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_PRECHECK_TEXT, ) def test_security_request_error_thrown_on_surrogat_error( @@ -41,7 +41,7 @@ def test_security_request_error_thrown_on_surrogat_error( self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_NULL_RESPONSE_SURROGAT_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT, ) def test_security_request_error_thrown_on_miscellaneous_error( @@ -53,5 +53,5 @@ def test_security_request_error_thrown_on_miscellaneous_error( self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_NULL_RESPONSE_GENERIC_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_GENERIC_TEXT, ) diff --git a/tests/test_runner.py b/tests/test_runner.py index 11daa833..214e1a6c 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -11,9 +11,9 @@ from tests.access.test_access_request_builder import TestAccessRequestBuilder from tests.access.test_access_result_parser import TestAccessResultParser from tests.common.test_customize_segment_traits import TestCustomizeSegmentTraits +from tests.common.test_downstream_fatal_error import TestDownstreamFatalError from tests.common.test_logger import TestLogger from tests.common.test_run_as_userid import TestRunAsUserId -from tests.common.test_security_request_error import TestDownstreamFatalError from tests.common.test_setup_precheck import TestSetupPrecheck from tests.connection.test_connection_debug_logging import TestConnectionDebugLogging from tests.connection.test_connection_request_builder import ( From d05d4c4a0bbac381ea84f2f0cb0191874b58352f Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 6 Dec 2023 11:56:03 -0500 Subject: [PATCH 17/30] Minor changes -Docstring Changes/Formatting updates -Move common test xml,json and log files into common directory -Attempted fix of build failures Signed-off-by: Elijah Swift --- pyracf/common/downstream_fatal_error.py | 24 +- pyracf/common/irrsmo00.py | 4 +- pyracf/common/logger.py | 12 +- pyracf/common/security_admin.py | 39 ++- pyracf/scripts/setup_precheck.py | 10 +- .../common_log_samples/alter_user_success.log | 263 ++++++++++++++++++ .../alter_user_success_as_eswift.log | 0 .../extract_resource_precheck_as_squidwrd.log | 0 .../alter_user_request.xml | 11 + .../alter_user_request_replace_segments.xml | 0 .../alter_user_request_update_segments.xml | 0 ...rce_result_precheck_uacc_none_success.json | 0 ...urce_result_precheck_uacc_none_success.xml | 0 .../alter_user_result_success.json | 23 ++ .../alter_user_result_success.xml | 14 + ...extract_resource_result_precheck_error.xml | 0 ...tract_resource_result_precheck_success.xml | 0 ..._user_result_base_omvs_csdata_success.json | 0 ...t_user_result_base_omvs_csdata_success.xml | 0 ...extract_user_result_base_only_success.json | 57 ++++ .../extract_user_result_base_only_success.xml | 32 +++ tests/common/test_common_constants.py | 54 ++-- tests/common/test_downstream_fatal_error.py | 2 +- tests/common/test_run_as_userid.py | 2 +- tests/common/test_setup_precheck.py | 6 +- tests/test_utilities.py | 2 +- 26 files changed, 476 insertions(+), 79 deletions(-) create mode 100644 tests/common/common_log_samples/alter_user_success.log rename tests/{user/user_log_samples => common/common_log_samples}/alter_user_success_as_eswift.log (100%) rename tests/{resource/resource_log_samples => common/common_log_samples}/extract_resource_precheck_as_squidwrd.log (100%) create mode 100644 tests/common/common_request_samples/alter_user_request.xml rename tests/{user/user_request_samples => common/common_request_samples}/alter_user_request_replace_segments.xml (100%) rename tests/{user/user_request_samples => common/common_request_samples}/alter_user_request_update_segments.xml (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/add_resource_result_precheck_uacc_none_success.json (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/add_resource_result_precheck_uacc_none_success.xml (100%) create mode 100644 tests/common/common_result_samples/alter_user_result_success.json create mode 100644 tests/common/common_result_samples/alter_user_result_success.xml rename tests/{resource/resource_result_samples => common/common_result_samples}/extract_resource_result_precheck_error.xml (100%) rename tests/{resource/resource_result_samples => common/common_result_samples}/extract_resource_result_precheck_success.xml (100%) rename tests/{user/user_result_samples => common/common_result_samples}/extract_user_result_base_omvs_csdata_success.json (100%) rename tests/{user/user_result_samples => common/common_result_samples}/extract_user_result_base_omvs_csdata_success.xml (100%) create mode 100644 tests/common/common_result_samples/extract_user_result_base_only_success.json create mode 100644 tests/common/common_result_samples/extract_user_result_base_only_success.xml diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index 48c81ed2..99ef504b 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -1,5 +1,5 @@ """Exception to use when IRRSMO00 is unable to process a request.""" -from typing import List, Union +from typing import Union class DownstreamFatalError(Exception): @@ -10,15 +10,17 @@ class DownstreamFatalError(Exception): def __init__( self, - return_reason_codes: List[int], + saf_return_code: int, + racf_return_code: int, + racf_reason_code: int, request_xml: bytes, run_as_userid: Union[str, None] = None, result_dictionary: dict = None, ) -> None: self.message = "Security request made to IRRSMO00 failed." - self.saf_return_code = return_reason_codes[0] - self.racf_return_code = return_reason_codes[1] - self.racf_reason_code = return_reason_codes[2] + self.saf_return_code = saf_return_code + self.racf_return_code = racf_return_code + self.racf_reason_code = racf_reason_code self.request_xml = request_xml.decode("utf-8") self.message += ( f"\n\nSAF Return Code: {self.saf_return_code}\nRACF Return Code:" @@ -29,7 +31,7 @@ def __init__( "\n\nSee results dictionary " + f"'{self.__class__.__name__}.result' for more details.\n" + "\n\nYou can also check the specified return and reason codes against " - + "the IRRSMO00 documented return and reason codes for more information " + + "the documented IRRSMO00 return and reason codes for more information " + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) @@ -41,8 +43,8 @@ def __init__( ): self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + + "For 'set' or 'alter' functions, you must have at least READ " + + "access to 'IRR.IRRSMO00.PRECHECK' in the 'XFACILIT' class." ) elif ( (self.saf_return_code == 8) @@ -51,13 +53,13 @@ def __init__( ): self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE " - + f"access to `{run_as_userid}.IRRSMO00` in the `SURROGAT` class." + + "For the 'run_as_userid' feature, you must have at least UPDATE " + + f"access to '{run_as_userid}.IRRSMO00' in the 'SURROGAT' class." ) else: self.message += ( "\n\nPlease check the specified return and reason codes against " - + "the IRRSMO00 documented return and reason codes for more information " + + "the documented IRRSMO00 return and reason codes for more information " + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index ab92e058..6f9174fc 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -39,7 +39,7 @@ def call_racf( opts=options, userid=userid, userid_len=userid_length, - ).decode("cp1047") + ) if response[0] == "": return response[1:3] - return response[0] + return response[0].decode("cp1047") diff --git a/pyracf/common/logger.py b/pyracf/common/logger.py index 59bd2200..f86052d0 100644 --- a/pyracf/common/logger.py +++ b/pyracf/common/logger.py @@ -161,7 +161,7 @@ def redact_request_xml( def redact_result_xml( self, - xml_response: Union[str, List[int]], + racf_response: Union[str, List[int]], secret_traits: dict, ) -> str: """ @@ -170,15 +170,15 @@ def redact_result_xml( 'TRAIT (value)' This function also accounts for varied amounts of whitespace in the pattern. """ - if isinstance(xml_response, list): - return xml_response + if isinstance(racf_response, list): + return racf_response for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - match = re.search(rf"{racf_key.upper()} +\(", xml_response) + match = re.search(rf"{racf_key.upper()} +\(", racf_response) if not match: continue - xml_response = self.__redact_string(xml_response, match.end(), ")") - return xml_response + racf_response = self.__redact_string(racf_response, match.end(), ")") + return racf_response def __colorize_json(self, json_text: str) -> str: updated_json_text = "" diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index f78fffd3..b7011711 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -90,15 +90,12 @@ def __init__( # ============================================================================ def set_running_userid(self, userid: Union[str, None]) -> None: if userid is None: - self.__running_userid = userid + self.__running_userid = None return if not isinstance(userid, str) or len(userid) > 8 or userid == "": raise UserIdError(userid) self.__running_userid = userid.upper() - def clear_running_userid(self) -> None: - self.__running_userid = None - def get_running_userid(self) -> None: return self.__running_userid @@ -186,7 +183,7 @@ def _make_request( if self._generate_requests_only: self.__clear_state(security_request) return request_xml - result_xml = self.__logger.redact_result_xml( + raw_result = self.__logger.redact_result_xml( self.__irrsmo00.call_racf( security_request.dump_request_xml(), irrsmo00_precheck, @@ -195,46 +192,46 @@ def _make_request( self.__secret_traits, ) self.__clear_state(security_request) - if isinstance(result_xml, list): + if isinstance(raw_result, list): # When IRRSMO00 encounters some errors, it returns no XML response string. # When this happens, the C code instead surfaces the return and reason # codes which causes a DownstreamFatalError to be raised. raise DownstreamFatalError( - return_reason_codes=result_xml, + saf_return_code=raw_result[0], + racf_return_code=raw_result[1], + racf_reason_code=raw_result[2], request_xml=request_xml, run_as_userid=self.get_running_userid(), ) if self.__debug: # No need to redact anything here since the raw result XML # already has secrets redacted when it is built. - self.__logger.log_xml("Result XML", result_xml) - results = SecurityResult(result_xml, self.get_running_userid()) + self.__logger.log_xml("Result XML", raw_result) + result = SecurityResult(raw_result, self.get_running_userid()) if self.__debug: # No need to redact anything here since the result dictionary # already has secrets redacted when it is built. self.__logger.log_dictionary( - "Result Dictionary", results.get_result_dictionary() + "Result Dictionary", result.get_result_dictionary() ) - result_dictionary = results.get_result_dictionary() - if result_dictionary["securityResult"]["returnCode"] > 4: + result_dictionary = result.get_result_dictionary() + if result_dictionary["securityResult"]["returnCode"] >= 8: # All return codes greater than 4 are indicative of an issue with # IRRSMO00 that would stop a command image from being generated. # The user should interogate the result dictionary attached to the # SecurityRequestError as well as the return and reason codes to # resolve the problem. raise DownstreamFatalError( - [ - 8, - result_dictionary["securityResult"]["returnCode"], - result_dictionary["securityResult"]["reasonCode"], - ], - request_xml, - self.get_running_userid(), - result_dictionary, + saf_return_code=8, + racf_return_code=result_dictionary["securityResult"]["returnCode"], + racf_reason_code=result_dictionary["securityResult"]["reasonCode"], + request_xml=request_xml, + run_as_userid=self.get_running_userid(), + result_dictionary=result_dictionary, ) if result_dictionary["securityResult"]["returnCode"] != 0: # All non-zero return codes should cause a SecurityRequestError to be raised. - # Even if a return code of 4 is not indicative of a problem, it it is + # Even if a return code of 4 is not indicative of a problem, it is # up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. diff --git a/pyracf/scripts/setup_precheck.py b/pyracf/scripts/setup_precheck.py index 95c26d87..2132fd61 100644 --- a/pyracf/scripts/setup_precheck.py +++ b/pyracf/scripts/setup_precheck.py @@ -11,16 +11,16 @@ def setup_precheck(): "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck ) print( - "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None.\n" - + "Contact your security administrator for READ access before using pyRACF.\n" + "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of None.\n" + + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Other users of pyRACF will also need to have at least read access.\n" - + "You may also need to REFRESH the `XFACILIT` class.\n" + + "You may also need to REFRESH the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return result if access: print( - f"IRR.IRRSMO00.PRECHECK is already defined, and you already have {access} access!\n" + f"IRR.IRRSMO00.PRECHECK is already defined, and you already have '{access}' access!\n" + "You are ready to start using pyRACF!\n" + "Please ensure other users of pyRACF also have at least read access.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" @@ -28,7 +28,7 @@ def setup_precheck(): return True print( "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" - + "Contact your security administrator for READ access before using pyRACF.\n" + + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return False diff --git a/tests/common/common_log_samples/alter_user_success.log b/tests/common/common_log_samples/alter_user_success.log new file mode 100644 index 00000000..b6319288 --- /dev/null +++ b/tests/common/common_log_samples/alter_user_success.log @@ -0,0 +1,263 @@ + + [pyRACF:Debug] + Request Dictionary + UserAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + UserAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + UserAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTUSER SQUIDWRD + USER=SQUIDWRD NAME=SQUIDWARD OWNER=LEONARD CREATED=23.094 + DEFAULT-GROUP=SYS1 PASSDATE=00.000 PASS-INTERVAL=186 PHRASEDATE=N/A + ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + LAST-ACCESS=23.094/12:55:37 + CLASS AUTHORIZATIONS=NONE + NO-INSTALLATION-DATA + NO-MODEL-NAME + LOGON ALLOWED (DAYS) (TIME) + --------------------------------------------- + ANYDAY ANYTIME + GROUP=SYS1 AUTH=USE CONNECT-OWNER=LEONARD CONNECT-DATE=23.094 + CONNECTS= 00 UACC=NONE LAST-CONNECT=UNKNOWN + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + SECURITY-LEVEL=NONE SPECIFIED + CATEGORY-AUTHORIZATION + NONE SPECIFIED + SECURITY-LABEL=NONE SPECIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + UserAdmin.alter() + + +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "listdata", + "requestId": "UserRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTUSER SQUIDWRD ", + "messages": [ + "USER=SQUIDWRD NAME=SQUIDWARD OWNER=LEONARD CREATED=23.094", + " DEFAULT-GROUP=SYS1 PASSDATE=00.000 PASS-INTERVAL=186 PHRASEDATE=N/A", + " ATTRIBUTES=NONE", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LAST-ACCESS=23.094/12:55:37", + " CLASS AUTHORIZATIONS=NONE", + " NO-INSTALLATION-DATA", + " NO-MODEL-NAME", + " LOGON ALLOWED (DAYS) (TIME)", + " ---------------------------------------------", + " ANYDAY ANYTIME", + " GROUP=SYS1 AUTH=USE CONNECT-OWNER=LEONARD CONNECT-DATE=23.094", + " CONNECTS= 00 UACC=NONE LAST-CONNECT=UNKNOWN", + " CONNECT ATTRIBUTES=NONE", + " REVOKE DATE=NONE RESUME DATE=NONE", + "SECURITY-LEVEL=NONE SPECIFIED", + "CATEGORY-AUTHORIZATION", + " NONE SPECIFIED", + "SECURITY-LABEL=NONE SPECIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + UserAdmin.alter() + + +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "listdata", + "requestId": "UserRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTUSER SQUIDWRD ", + "profiles": [ + { + "base": { + "user": "squidwrd", + "name": "squidward", + "owner": "leonard", + "created": "4/4/2023", + "defaultGroup": "sys1", + "passwordDate": null, + "passwordInterval": 186, + "passphraseDate": null, + "attributes": [], + "revokeDate": null, + "resumeDate": null, + "lastAccess": "4/4/2023 12:55 PM", + "classAuthorizations": [], + "logonAllowedDays": "anyday", + "logonAllowedTime": "anytime", + "groups": { + "SYS1": { + "auth": "use", + "connectOwner": "leonard", + "connectDate": "4/4/2023", + "connects": 0, + "uacc": null, + "lastConnect": null, + "connectAttributes": [], + "revokeDate": null, + "resumeDate": null + } + }, + "securityLevel": null, + "categoryAuthorization": null, + "securityLabel": null + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + + + [pyRACF:Debug] + Request Dictionary + UserAdmin.alter() + + +{ + "base": { + "base:special": { + "value": false, + "operation": "delete" + } + }, + "omvs": { + "omvs:home_directory": { + "value": "/u/clarinet", + "operation": null + }, + "omvs:default_shell": { + "value": false, + "operation": "delete" + } + } +} + + + [pyRACF:Debug] + Request XML + UserAdmin.alter() + + + + + + + + + /u/clarinet + + + + + + + [pyRACF:Debug] + Result XML + UserAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM ) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + UserAdmin.alter() + + +{ + "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 NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM )" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} + diff --git a/tests/user/user_log_samples/alter_user_success_as_eswift.log b/tests/common/common_log_samples/alter_user_success_as_eswift.log similarity index 100% rename from tests/user/user_log_samples/alter_user_success_as_eswift.log rename to tests/common/common_log_samples/alter_user_success_as_eswift.log diff --git a/tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log b/tests/common/common_log_samples/extract_resource_precheck_as_squidwrd.log similarity index 100% rename from tests/resource/resource_log_samples/extract_resource_precheck_as_squidwrd.log rename to tests/common/common_log_samples/extract_resource_precheck_as_squidwrd.log diff --git a/tests/common/common_request_samples/alter_user_request.xml b/tests/common/common_request_samples/alter_user_request.xml new file mode 100644 index 00000000..39340346 --- /dev/null +++ b/tests/common/common_request_samples/alter_user_request.xml @@ -0,0 +1,11 @@ + + + + + + + /u/clarinet + + + + \ No newline at end of file diff --git a/tests/user/user_request_samples/alter_user_request_replace_segments.xml b/tests/common/common_request_samples/alter_user_request_replace_segments.xml similarity index 100% rename from tests/user/user_request_samples/alter_user_request_replace_segments.xml rename to tests/common/common_request_samples/alter_user_request_replace_segments.xml diff --git a/tests/user/user_request_samples/alter_user_request_update_segments.xml b/tests/common/common_request_samples/alter_user_request_update_segments.xml similarity index 100% rename from tests/user/user_request_samples/alter_user_request_update_segments.xml rename to tests/common/common_request_samples/alter_user_request_update_segments.xml diff --git a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_success.json similarity index 100% rename from tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.json rename to tests/common/common_result_samples/add_resource_result_precheck_uacc_none_success.json diff --git a/tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_success.xml similarity index 100% rename from tests/resource/resource_result_samples/add_resource_result_precheck_uacc_none_success.xml rename to tests/common/common_result_samples/add_resource_result_precheck_uacc_none_success.xml diff --git a/tests/common/common_result_samples/alter_user_result_success.json b/tests/common/common_result_samples/alter_user_result_success.json new file mode 100644 index 00000000..290032c6 --- /dev/null +++ b/tests/common/common_result_samples/alter_user_result_success.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 NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM )" + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/common/common_result_samples/alter_user_result_success.xml b/tests/common/common_result_samples/alter_user_result_success.xml new file mode 100644 index 00000000..e8492834 --- /dev/null +++ b/tests/common/common_result_samples/alter_user_result_success.xml @@ -0,0 +1,14 @@ + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTUSER SQUIDWRD NOSPECIAL OMVS (HOME ('/u/clarinet') NOPROGRAM ) + + + 0 + 0 + \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml b/tests/common/common_result_samples/extract_resource_result_precheck_error.xml similarity index 100% rename from tests/resource/resource_result_samples/extract_resource_result_precheck_error.xml rename to tests/common/common_result_samples/extract_resource_result_precheck_error.xml diff --git a/tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml b/tests/common/common_result_samples/extract_resource_result_precheck_success.xml similarity index 100% rename from tests/resource/resource_result_samples/extract_resource_result_precheck_success.xml rename to tests/common/common_result_samples/extract_resource_result_precheck_success.xml diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json b/tests/common/common_result_samples/extract_user_result_base_omvs_csdata_success.json similarity index 100% rename from tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.json rename to tests/common/common_result_samples/extract_user_result_base_omvs_csdata_success.json diff --git a/tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.xml b/tests/common/common_result_samples/extract_user_result_base_omvs_csdata_success.xml similarity index 100% rename from tests/user/user_result_samples/extract_user_result_base_omvs_csdata_success.xml rename to tests/common/common_result_samples/extract_user_result_base_omvs_csdata_success.xml diff --git a/tests/common/common_result_samples/extract_user_result_base_only_success.json b/tests/common/common_result_samples/extract_user_result_base_only_success.json new file mode 100644 index 00000000..20ffb3b5 --- /dev/null +++ b/tests/common/common_result_samples/extract_user_result_base_only_success.json @@ -0,0 +1,57 @@ +{ + "securityResult": { + "user": { + "name": "SQUIDWRD", + "operation": "listdata", + "requestId": "UserRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTUSER SQUIDWRD ", + "profiles": [ + { + "base": { + "user": "squidwrd", + "name": "squidward", + "owner": "leonard", + "created": "4/4/2023", + "defaultGroup": "sys1", + "passwordDate": null, + "passwordInterval": 186, + "passphraseDate": null, + "attributes": [], + "revokeDate": null, + "resumeDate": null, + "lastAccess": "4/4/2023 12:55 PM", + "classAuthorizations": [], + "logonAllowedDays": "anyday", + "logonAllowedTime": "anytime", + "groups": { + "SYS1": { + "auth": "use", + "connectOwner": "leonard", + "connectDate": "4/4/2023", + "connects": 0, + "uacc": null, + "lastConnect": null, + "connectAttributes": [], + "revokeDate": null, + "resumeDate": null + } + }, + "securityLevel": null, + "categoryAuthorization": null, + "securityLabel": null + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/common/common_result_samples/extract_user_result_base_only_success.xml b/tests/common/common_result_samples/extract_user_result_base_only_success.xml new file mode 100644 index 00000000..61a46535 --- /dev/null +++ b/tests/common/common_result_samples/extract_user_result_base_only_success.xml @@ -0,0 +1,32 @@ + + + + + 0 + 0 + 0 + LISTUSER SQUIDWRD + USER=SQUIDWRD NAME=SQUIDWARD OWNER=LEONARD CREATED=23.094 + DEFAULT-GROUP=SYS1 PASSDATE=00.000 PASS-INTERVAL=186 PHRASEDATE=N/A + ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + LAST-ACCESS=23.094/12:55:37 + CLASS AUTHORIZATIONS=NONE + NO-INSTALLATION-DATA + NO-MODEL-NAME + LOGON ALLOWED (DAYS) (TIME) + --------------------------------------------- + ANYDAY ANYTIME + GROUP=SYS1 AUTH=USE CONNECT-OWNER=LEONARD CONNECT-DATE=23.094 + CONNECTS= 00 UACC=NONE LAST-CONNECT=UNKNOWN + CONNECT ATTRIBUTES=NONE + REVOKE DATE=NONE RESUME DATE=NONE + SECURITY-LEVEL=NONE SPECIFIED + CATEGORY-AUTHORIZATION + NONE SPECIFIED + SECURITY-LABEL=NONE SPECIFIED + + + 0 + 0 + \ No newline at end of file diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 2e9042e9..3541539b 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -7,8 +7,8 @@ import tests.test_utilities as TestUtilities -def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: - return TestUtilities.get_sample(sample_file, function_group) +def get_sample(sample_file: str) -> Union[str, bytes]: + return TestUtilities.get_sample(sample_file, "common") # ============================================================================ @@ -16,21 +16,21 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: # ============================================================================ TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML = get_sample( - "extract_resource_result_precheck_error.xml", "resource" + "extract_resource_result_precheck_error.xml" ) TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML = get_sample( - "extract_resource_result_precheck_success.xml", "resource" + "extract_resource_result_precheck_success.xml" ) TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_XML = get_sample( - "add_resource_result_precheck_uacc_none_success.xml", "resource" + "add_resource_result_precheck_uacc_none_success.xml" ) TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY = get_sample( - "add_resource_result_precheck_uacc_none_success.json", "resource" + "add_resource_result_precheck_uacc_none_success.json" ) TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, and you already have alter access!\n" + "IRR.IRRSMO00.PRECHECK is already defined, and you already have 'alter' access!\n" + "You are ready to start using pyRACF!\n" + "Please ensure other users of pyRACF also have at least read access.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" @@ -38,15 +38,15 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: TEST_SETUP_PRECHECK_FOUND_NO_ACCESS_TEXT = ( "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" - + "Contact your security administrator for READ access before using pyRACF.\n" + + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT = ( - "IRR.IRRSMO00.PRECHECK is now defined with a `Universal Access` of None.\n" - + "Contact your security administrator for READ access before using pyRACF.\n" + "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of None.\n" + + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Other users of pyRACF will also need to have at least read access.\n" - + "You may also need to REFRESH the `XFACILIT` class.\n" + + "You may also need to REFRESH the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) @@ -76,18 +76,18 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: # Alter User Requests TEST_ALTER_USER_REQUEST_REPLACE_SEGMENTS_XML = get_sample( - "alter_user_request_replace_segments.xml", "user" + "alter_user_request_replace_segments.xml" ) TEST_ALTER_USER_REQUEST_UPDATE_SEGMENTS_XML = get_sample( - "alter_user_request_update_segments.xml", "user" + "alter_user_request_update_segments.xml" ) # Extract User Results TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_XML = get_sample( - "extract_user_result_base_omvs_csdata_success.xml", "user" + "extract_user_result_base_omvs_csdata_success.xml" ) TEST_EXTRACT_USER_RESULT_BASE_OMVS_CSDATA_SUCCESS_DICTIONARY = get_sample( - "extract_user_result_base_omvs_csdata_success.json", "user" + "extract_user_result_base_omvs_csdata_success.json" ) # ============================================================================ @@ -100,17 +100,15 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: "omvs:home_directory": "/u/clarinet", "omvs:default_shell": False, } -TEST_ALTER_USER_REQUEST_XML = get_sample("alter_user_request.xml", "user") +TEST_ALTER_USER_REQUEST_XML = get_sample("alter_user_request.xml") TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML = get_sample( - "extract_user_result_base_only_success.xml", "user" + "extract_user_result_base_only_success.xml" ) -TEST_ALTER_USER_SUCCESS_LOG = get_sample("alter_user_success.log", "user") -TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG = get_sample( - "alter_user_success_as_eswift.log", "user" -) -TEST_ALTER_USER_RESULT_SUCCESS_XML = get_sample("alter_user_result_success.xml", "user") +TEST_ALTER_USER_SUCCESS_LOG = get_sample("alter_user_success.log") +TEST_ALTER_USER_SUCCESS_AS_ESWIFT_LOG = get_sample("alter_user_success_as_eswift.log") +TEST_ALTER_USER_RESULT_SUCCESS_XML = get_sample("alter_user_result_success.xml") TEST_EXTRACT_RESOURCE_PRECHECK_AS_SQUIDWRD_LOG = get_sample( - "extract_resource_precheck_as_squidwrd.log", "resource" + "extract_resource_precheck_as_squidwrd.log" ) # ============================================================================ @@ -121,23 +119,23 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For `set` or `alter` functions, you must have at least READ " - + "access to `IRR.IRRSMO00.PRECHECK` in the `XFACILIT` class." + + "For 'set' or 'alter' functions, you must have at least READ " + + "access to 'IRR.IRRSMO00.PRECHECK' in the 'XFACILIT' class." ) TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the `run_as_userid` feature, you must have at least UPDATE " - + "access to `ESWIFT.IRRSMO00` in the `SURROGAT` class." + + "For the 'run_as_userid' feature, you must have at least UPDATE " + + "access to 'ESWIFT.IRRSMO00' in the 'SURROGAT' class." ) TEST_DOWNSTREAM_FATAL_GENERIC_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against " - + "the IRRSMO00 documented return and reason codes for more information " + + "the documented IRRSMO00 return and reason codes for more information " + "about this error.\n" + "https://www.ibm.com/docs/en/zos/3.1.0?topic=operations-return-reason-codes" ) diff --git a/tests/common/test_downstream_fatal_error.py b/tests/common/test_downstream_fatal_error.py index 055ae89c..e582b9c6 100644 --- a/tests/common/test_downstream_fatal_error.py +++ b/tests/common/test_downstream_fatal_error.py @@ -1,4 +1,4 @@ -"""Test downstream fatal error raising.""" +"""Test downstream fatal error.""" import unittest from unittest.mock import Mock, patch diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py index 29548f31..7f267a6e 100644 --- a/tests/common/test_run_as_userid.py +++ b/tests/common/test_run_as_userid.py @@ -93,7 +93,7 @@ def test_clear_running_userid( call_racf_mock: Mock, ): user_admin = UserAdmin(debug=True, run_as_userid="ESWIFT") - user_admin.clear_running_userid() + user_admin.set_running_userid(userid=None) call_racf_mock.side_effect = [ TestCommonConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, TestCommonConstants.TEST_ALTER_USER_RESULT_SUCCESS_XML, diff --git a/tests/common/test_setup_precheck.py b/tests/common/test_setup_precheck.py index 7af95173..954f6b45 100644 --- a/tests/common/test_setup_precheck.py +++ b/tests/common/test_setup_precheck.py @@ -76,7 +76,7 @@ def test_setup_precheck_works_when_control_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_control_access = validated_control_access.replace( - "you already have alter access!", "you already have control access!" + "you already have 'alter' access!", "you already have 'control' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -101,7 +101,7 @@ def test_setup_precheck_works_when_read_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_read_access = validated_read_access.replace( - "you already have alter access!", "you already have read access!" + "you already have 'alter' access!", "you already have 'read' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -126,7 +126,7 @@ def test_setup_precheck_works_when_update_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_update_access = validated_update_access.replace( - "you already have alter access!", "you already have update access!" + "you already have 'alter' access!", "you already have 'update' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): diff --git a/tests/test_utilities.py b/tests/test_utilities.py index db14398a..6e5398d8 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -57,7 +57,7 @@ def get_sample(sample_file: str, function_group: str) -> Union[str, bytes]: result_samples = f"{function_group}/{function_group}_result_samples" request_samples = f"{function_group}/{function_group}_request_samples" log_samples = f"{function_group}/{function_group}_log_samples" - if f"{function_group}_result" in sample_file: + if "_result" in sample_file: result_sample_file = f"{result_samples}/{sample_file}" if sample_file[-4:] == ".xml": return get_xml(result_sample_file) From 2baa0801ba1b6b949ac43fdbb72ad7eb63690d18 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 6 Dec 2023 17:54:31 -0500 Subject: [PATCH 18/30] Minor changes Naming changes text updates Signed-off-by: Elijah Swift --- pyracf/common/downstream_fatal_error.py | 4 ++-- pyracf/common/logger.py | 14 ++++++++------ pyracf/common/security_admin.py | 6 +++--- pyracf/scripts/setup_precheck.py | 9 +++++---- tests/common/test_common_constants.py | 12 ++++++------ tests/common/test_run_as_userid.py | 14 +------------- tests/common/test_setup_precheck.py | 6 +++--- 7 files changed, 28 insertions(+), 37 deletions(-) diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index 99ef504b..25cc86b8 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -43,7 +43,7 @@ def __init__( ): self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For 'set' or 'alter' functions, you must have at least READ " + + "For 'set' or 'alter' functions, you must have at least 'READ' " + "access to 'IRR.IRRSMO00.PRECHECK' in the 'XFACILIT' class." ) elif ( @@ -53,7 +53,7 @@ def __init__( ): self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the 'run_as_userid' feature, you must have at least UPDATE " + + "For the 'run_as_userid' feature, you must have at least 'UPDATE' " + f"access to '{run_as_userid}.IRRSMO00' in the 'SURROGAT' class." ) else: diff --git a/pyracf/common/logger.py b/pyracf/common/logger.py index f86052d0..d4b42eb0 100644 --- a/pyracf/common/logger.py +++ b/pyracf/common/logger.py @@ -161,7 +161,7 @@ def redact_request_xml( def redact_result_xml( self, - racf_response: Union[str, List[int]], + security_response: Union[str, List[int]], secret_traits: dict, ) -> str: """ @@ -170,15 +170,17 @@ def redact_result_xml( 'TRAIT (value)' This function also accounts for varied amounts of whitespace in the pattern. """ - if isinstance(racf_response, list): - return racf_response + if isinstance(security_response, list): + return security_response for xml_key in secret_traits.values(): racf_key = xml_key.split(":")[1] if ":" in xml_key else xml_key - match = re.search(rf"{racf_key.upper()} +\(", racf_response) + match = re.search(rf"{racf_key.upper()} +\(", security_response) if not match: continue - racf_response = self.__redact_string(racf_response, match.end(), ")") - return racf_response + security_response = self.__redact_string( + security_response, match.end(), ")" + ) + return security_response def __colorize_json(self, json_text: str) -> str: updated_json_text = "" diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index b7011711..cb3cef51 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -230,9 +230,9 @@ def _make_request( result_dictionary=result_dictionary, ) if result_dictionary["securityResult"]["returnCode"] != 0: - # All non-zero return codes should cause a SecurityRequestError to be raised. - # Even if a return code of 4 is not indicative of a problem, it is - # up to the user to interogate the result dictionary attached to the + # All remaining non-zero return codes should cause a SecurityRequestError + # to be raised. Even if a SAF return code of 4 is not indicative of a problem, + # it is up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. raise SecurityRequestError(result_dictionary) diff --git a/pyracf/scripts/setup_precheck.py b/pyracf/scripts/setup_precheck.py index 2132fd61..cd860a32 100644 --- a/pyracf/scripts/setup_precheck.py +++ b/pyracf/scripts/setup_precheck.py @@ -11,18 +11,19 @@ def setup_precheck(): "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck ) print( - "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of None.\n" + "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of 'NONE'.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" - + "Other users of pyRACF will also need to have at least read access.\n" + + "Other users of pyRACF will also need to have at least 'READ' access.\n" + "You may also need to REFRESH the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return result if access: print( - f"IRR.IRRSMO00.PRECHECK is already defined, and you already have '{access}' access!\n" + "IRR.IRRSMO00.PRECHECK is already defined, " + + f"and you already have '{access.upper()}' access!\n" + "You are ready to start using pyRACF!\n" - + "Please ensure other users of pyRACF also have at least read access.\n" + + "Please ensure other users of pyRACF also have at least 'READ' access.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return True diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 3541539b..bf45a5e8 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -30,9 +30,9 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, and you already have 'alter' access!\n" + "IRR.IRRSMO00.PRECHECK is already defined, and you already have 'ALTER' access!\n" + "You are ready to start using pyRACF!\n" - + "Please ensure other users of pyRACF also have at least read access.\n" + + "Please ensure other users of pyRACF also have at least 'READ' access.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) @@ -43,9 +43,9 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT = ( - "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of None.\n" + "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of 'NONE'.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" - + "Other users of pyRACF will also need to have at least read access.\n" + + "Other users of pyRACF will also need to have at least 'READ' access.\n" + "You may also need to REFRESH the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) @@ -119,7 +119,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For 'set' or 'alter' functions, you must have at least READ " + + "For 'set' or 'alter' functions, you must have at least 'READ' " + "access to 'IRR.IRRSMO00.PRECHECK' in the 'XFACILIT' class." ) @@ -127,7 +127,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" - + "For the 'run_as_userid' feature, you must have at least UPDATE " + + "For the 'run_as_userid' feature, you must have at least 'UPDATE' " + "access to 'ESWIFT.IRRSMO00' in the 'SURROGAT' class." ) diff --git a/tests/common/test_run_as_userid.py b/tests/common/test_run_as_userid.py index 7f267a6e..255c4667 100644 --- a/tests/common/test_run_as_userid.py +++ b/tests/common/test_run_as_userid.py @@ -138,21 +138,9 @@ def test_get_user_access( ) self.assertEqual(access, "read") - @patch("pyracf.common.irrsmo00.IRRSMO00.call_racf") - def test_get_user_access_raises_userid_error( - self, - call_racf_mock: Mock, - ): + def test_get_user_access_raises_userid_error(self): userid = "squidwrdtest" - precheck_profile_as_squidwrd = ( - TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_SUCCESS_XML - ) - precheck_profile_as_squidwrd = precheck_profile_as_squidwrd.replace( - " 00 ESWIFT READ ALTER NO", - " 00 ESWIFT READ READ NO", - ) resource_admin = ResourceAdmin(debug=True, run_as_userid="ESWIFT") - call_racf_mock.return_value = precheck_profile_as_squidwrd with self.assertRaises(UserIdError) as exception: resource_admin.get_user_access("IRR.IRRSMO00.PRECHECK", "XFACILIT", userid) self.assertEqual( diff --git a/tests/common/test_setup_precheck.py b/tests/common/test_setup_precheck.py index 954f6b45..8e4ff9d9 100644 --- a/tests/common/test_setup_precheck.py +++ b/tests/common/test_setup_precheck.py @@ -76,7 +76,7 @@ def test_setup_precheck_works_when_control_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_control_access = validated_control_access.replace( - "you already have 'alter' access!", "you already have 'control' access!" + "you already have 'ALTER' access!", "you already have 'CONTROL' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -101,7 +101,7 @@ def test_setup_precheck_works_when_read_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_read_access = validated_read_access.replace( - "you already have 'alter' access!", "you already have 'read' access!" + "you already have 'ALTER' access!", "you already have 'READ' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -126,7 +126,7 @@ def test_setup_precheck_works_when_update_access_exists( TestCommonConstants.TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT ) validated_update_access = validated_update_access.replace( - "you already have 'alter' access!", "you already have 'update' access!" + "you already have 'ALTER' access!", "you already have 'UPDATE' access!" ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): From 3c4bee2f583c911bd8fc4830637ee77f6ee756bc Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 12 Dec 2023 17:07:35 -0500 Subject: [PATCH 19/30] Add unit test for Add operations -Unit testing added for surfacing SecurityRequestErrors from initial extract in Add operations if the error is not "the resource does not yet exist" Signed-off-by: Elijah Swift --- tests/group/test_group_constants.py | 2 +- tests/group/test_group_result_parser.py | 32 +++++++++++++++++ tests/resource/test_resource_result_parser.py | 35 +++++++++++++++++++ tests/user/test_user_constants.py | 2 +- tests/user/test_user_result_parser.py | 32 +++++++++++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/tests/group/test_group_constants.py b/tests/group/test_group_constants.py index a971c3be..478a1ca9 100644 --- a/tests/group/test_group_constants.py +++ b/tests/group/test_group_constants.py @@ -51,7 +51,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_XML = get_sample( "extract_group_result_base_only_error.xml" ) -TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_JSON = get_sample( +TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_DICTIONARY = get_sample( "extract_group_result_base_only_error.json" ) TEST_EXTRACT_GROUP_RESULT_BAD_ATTRIBUTE_ERROR_XML = get_sample( diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index 69054faa..deaab8c4 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -1,5 +1,6 @@ """Test group result parser.""" +import copy import unittest from unittest.mock import Mock, patch @@ -63,6 +64,37 @@ def test_group_admin_throws_error_on_add_existing_group( + f"'{group_name}' already exists as a '{self.group_admin._profile_type}' profile.", ) + def test_group_admin_add_surfaces_error_from_preliminary_extract( + self, + call_racf_mock: Mock, + ): + group_name = "TESTGRP0" + extract_group_error_xml = ( + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_XML + ) + extract_group_error_xml = extract_group_error_xml.replace( + "ICH51003I NAME NOT FOUND IN RACF DATA SET", + "NOT A REAL ERROR MESSAGE", + ) + call_racf_mock.side_effect = [ + extract_group_error_xml, + TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + self.group_admin.add( + group_name, traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + ) + extract_group_error_json = copy.deepcopy( + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_DICTIONARY + ) + extract_group_error_json["securityResult"]["group"]["commands"][0][ + "messages" + ] = ["NOT A REAL ERROR MESSAGE"] + self.assertEqual( + exception.exception.result, + extract_group_error_json, + ) + # Error in command, TESTGRPP0 is bad GROUP def test_group_admin_can_parse_add_group_error_xml( self, diff --git a/tests/resource/test_resource_result_parser.py b/tests/resource/test_resource_result_parser.py index 525366ee..0e09c0a5 100644 --- a/tests/resource/test_resource_result_parser.py +++ b/tests/resource/test_resource_result_parser.py @@ -1,5 +1,6 @@ """Test general resource profile result parser.""" +import copy import unittest from unittest.mock import Mock, patch @@ -63,6 +64,40 @@ def test_resource_admin_throws_error_on_add_existing_profile( + f"'{profile_name}' already exists as a profile in the '{class_name}' class.", ) + def test_resource_admin_add_surfaces_error_from_preliminary_extract( + self, + call_racf_mock: Mock, + ): + profile_name = "TESTING" + class_name = "ELIJTEST" + extract_resource_xml = ( + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML + ) + extract_resource_xml = extract_resource_xml.replace( + "ICH13003I TESTING NOT FOUND", + "NOT A REAL ERROR MESSAGE", + ) + call_racf_mock.side_effect = [ + extract_resource_xml, + TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + self.resource_admin.add( + profile_name, + class_name, + traits=TestResourceConstants.TEST_ADD_RESOURCE_REQUEST_TRAITS, + ) + extract_resource_error_json = copy.deepcopy( + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_DICTIONARY + ) + extract_resource_error_json["securityResult"]["resource"]["commands"][0][ + "messages" + ] = ["NOT A REAL ERROR MESSAGE"] + self.assertEqual( + exception.exception.result, + extract_resource_error_json, + ) + def test_resource_admin_avoids_error_on_add_covered_profile( self, call_racf_mock: Mock, diff --git a/tests/user/test_user_constants.py b/tests/user/test_user_constants.py index 7188a912..5129946f 100644 --- a/tests/user/test_user_constants.py +++ b/tests/user/test_user_constants.py @@ -94,7 +94,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_XML = get_sample( "extract_user_result_base_only_error.xml" ) -TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_JSON = get_sample( +TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_DICTIONARY = get_sample( "extract_user_result_base_only_error.json" ) TEST_EXTRACT_USER_RESULT_WITH_CLASS_AUTHORIZATIONS = get_sample( diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index a4b483ca..367bc01d 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -1,5 +1,6 @@ """Test user result parser.""" +import copy import unittest from unittest.mock import Mock, patch @@ -67,6 +68,37 @@ def test_user_admin_throws_error_on_add_existing_user( + f"'{profile_name}' already exists as a '{self.user_admin._profile_type}' profile.", ) + def test_user_admin_add_surfaces_error_from_preliminary_extract( + self, + call_racf_mock: Mock, + ): + profile_name = "squidwrd" + extract_user_error_xml = ( + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_XML + ) + extract_user_error_xml = extract_user_error_xml.replace( + "ICH30001I UNABLE TO LOCATE USER ENTRY SQUIDWRD", + "NOT A REAL ERROR MESSAGE", + ) + call_racf_mock.side_effect = [ + extract_user_error_xml, + TestUserConstants.TEST_ADD_USER_RESULT_SUCCESS_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + self.user_admin.add( + profile_name, traits=TestUserConstants.TEST_ADD_USER_REQUEST_TRAITS + ) + extract_user_error_json = copy.deepcopy( + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_DICTIONARY + ) + extract_user_error_json["securityResult"]["user"]["commands"][0]["messages"] = [ + "NOT A REAL ERROR MESSAGE" + ] + self.assertEqual( + exception.exception.result, + extract_user_error_json, + ) + # Error in command, SQUIDWARD is bad USERID def test_user_admin_can_parse_add_user_error_xml( self, From a9584018f13077f6247bd49e70a2b653eabefd21 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Mon, 18 Dec 2023 16:25:20 -0500 Subject: [PATCH 20/30] Docstring changes Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 2 +- pyracf/common/security_request_error.py | 5 +++-- pyracf/scripts/setup_precheck.py | 8 ++++---- .../extract_resource_result_precheck_error.xml | 2 +- tests/common/test_common_constants.py | 8 ++++---- tests/common/test_downstream_fatal_error.py | 6 +++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index cb3cef51..36960d0c 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -231,7 +231,7 @@ def _make_request( ) if result_dictionary["securityResult"]["returnCode"] != 0: # All remaining non-zero return codes should cause a SecurityRequestError - # to be raised. Even if a SAF return code of 4 is not indicative of a problem, + # to be raised. Even if a return code of 4 is not indicative of a problem, # it is up to the user to interogate the result dictionary attached to the # SecurityRequestError and decided whether or not the return code 4 is # indicative of a problem. diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 13a77e73..73e85959 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -3,7 +3,8 @@ class SecurityRequestError(Exception): """ - Raised when the SAF Return Code of a security result returned by IRRSMO00 is equal to 4. + Raised when pyRACF does not rais a DownstreamFatalError, and the SAF Return Code + of a security result returned by IRRSMO00 is non-zero. """ def __init__(self, result: dict) -> None: @@ -21,7 +22,7 @@ def __str__(self) -> str: def contains_error_message( self, security_definition_tag: str, error_message_id: str ): - """Checks to see if specific error message id appears in the security request error""" + """Checks to see if specific error message id appears in the security request error.""" commands = self.result["securityResult"][security_definition_tag].get( "commands" ) diff --git a/pyracf/scripts/setup_precheck.py b/pyracf/scripts/setup_precheck.py index cd860a32..998e08a7 100644 --- a/pyracf/scripts/setup_precheck.py +++ b/pyracf/scripts/setup_precheck.py @@ -11,16 +11,16 @@ def setup_precheck(): "IRR.IRRSMO00.PRECHECK", "XFACILIT", traits=traits_precheck ) print( - "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of 'NONE'.\n" + "'IRR.IRRSMO00.PRECHECK' is now defined with a 'Universal Access' of 'NONE'.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Other users of pyRACF will also need to have at least 'READ' access.\n" - + "You may also need to REFRESH the 'XFACILIT' class.\n" + + "You may also need to refresh the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) return result if access: print( - "IRR.IRRSMO00.PRECHECK is already defined, " + "'IRR.IRRSMO00.PRECHECK' is already defined, " + f"and you already have '{access.upper()}' access!\n" + "You are ready to start using pyRACF!\n" + "Please ensure other users of pyRACF also have at least 'READ' access.\n" @@ -28,7 +28,7 @@ def setup_precheck(): ) return True print( - "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" + "'IRR.IRRSMO00.PRECHECK' is already defined, but you have no access.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!" ) diff --git a/tests/common/common_result_samples/extract_resource_result_precheck_error.xml b/tests/common/common_result_samples/extract_resource_result_precheck_error.xml index 9621521e..abcdfd89 100644 --- a/tests/common/common_result_samples/extract_resource_result_precheck_error.xml +++ b/tests/common/common_result_samples/extract_resource_result_precheck_error.xml @@ -6,7 +6,7 @@ 16 4 RLIST XFACILIT (IRR.IRRSMO00.PRECHECK) - ICH13003I IRR.IRRSMO00.PRECHECK NOT FOUND + ICH13003I 'IRR.IRRSMO00.PRECHECK' NOT FOUND 4 diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index bf45a5e8..6761f191 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -30,23 +30,23 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, and you already have 'ALTER' access!\n" + "'IRR.IRRSMO00.PRECHECK' is already defined, and you already have 'ALTER' access!\n" + "You are ready to start using pyRACF!\n" + "Please ensure other users of pyRACF also have at least 'READ' access.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) TEST_SETUP_PRECHECK_FOUND_NO_ACCESS_TEXT = ( - "IRR.IRRSMO00.PRECHECK is already defined, but you have no access.\n" + "'IRR.IRRSMO00.PRECHECK' is already defined, but you have no access.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) TEST_SETUP_PRECHECK_DEFINED_PROFILE_TEXT = ( - "IRR.IRRSMO00.PRECHECK is now defined with a 'Universal Access' of 'NONE'.\n" + "'IRR.IRRSMO00.PRECHECK' is now defined with a 'Universal Access' of 'NONE'.\n" + "Contact your security administrator for 'READ' access before using pyRACF.\n" + "Other users of pyRACF will also need to have at least 'READ' access.\n" - + "You may also need to REFRESH the 'XFACILIT' class.\n" + + "You may also need to refresh the 'XFACILIT' class.\n" + "Review our documentation at https://ambitus.github.io/pyracf/ as well!\n" ) diff --git a/tests/common/test_downstream_fatal_error.py b/tests/common/test_downstream_fatal_error.py index e582b9c6..86b8f485 100644 --- a/tests/common/test_downstream_fatal_error.py +++ b/tests/common/test_downstream_fatal_error.py @@ -19,7 +19,7 @@ class TestDownstreamFatalError(unittest.TestCase): IRRSMO00.__init__ = Mock(return_value=None) user_admin = UserAdmin() - def test_security_request_error_thrown_on_precheck_error( + def test_donwstream_fatal_error_thrown_on_precheck_error( self, call_racf_mock: Mock, ): @@ -31,7 +31,7 @@ def test_security_request_error_thrown_on_precheck_error( TestCommonConstants.TEST_DOWNSTREAM_FATAL_PRECHECK_TEXT, ) - def test_security_request_error_thrown_on_surrogat_error( + def test_donwstream_fatal_error_thrown_on_surrogat_error( self, call_racf_mock: Mock, ): @@ -44,7 +44,7 @@ def test_security_request_error_thrown_on_surrogat_error( TestCommonConstants.TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT, ) - def test_security_request_error_thrown_on_miscellaneous_error( + def test_donwstream_fatal_error_thrown_on_miscellaneous_error( self, call_racf_mock: Mock, ): From dbdf9c121b6d332809f88621ee2cffd829a1ea42 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 19 Dec 2023 10:56:33 -0500 Subject: [PATCH 21/30] Add Testcase for SetupPrecheck error Signed-off-by: Elijah Swift --- pyracf/common/security_request_error.py | 2 +- ...ource_result_precheck_uacc_none_error.json | 33 +++++++++++++++++++ ...source_result_precheck_uacc_none_error.xml | 21 ++++++++++++ tests/common/test_common_constants.py | 7 ++++ tests/common/test_setup_precheck.py | 18 +++++++++- 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.json create mode 100644 tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.xml diff --git a/pyracf/common/security_request_error.py b/pyracf/common/security_request_error.py index 73e85959..6b4b4ae7 100644 --- a/pyracf/common/security_request_error.py +++ b/pyracf/common/security_request_error.py @@ -3,7 +3,7 @@ class SecurityRequestError(Exception): """ - Raised when pyRACF does not rais a DownstreamFatalError, and the SAF Return Code + Raised when pyRACF does not raise a DownstreamFatalError, and the SAF Return Code of a security result returned by IRRSMO00 is non-zero. """ diff --git a/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.json b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.json new file mode 100644 index 00000000..aecdcf5c --- /dev/null +++ b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.json @@ -0,0 +1,33 @@ +{ + "securityResult": { + "resource": { + "name": "IRR.IRRSMO00.PRECHECK", + "class": "XFACILIT", + "operation": "set", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "RDEFINE XFACILIT (IRR.IRRSMO00.PRECHECK) ", + "messages": [ + "ICH10103I NOT AUTHORIZED TO DEFINE IRR.IRRSMO00.PRECHECK." + ] + }, + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "RALTER XFACILIT (IRR.IRRSMO00.PRECHECK) UACC (None)", + "messages": [ + "ICH11102I IRR.IRRSMO00.PRECHECK NOT DEFINED TO CLASS XFACILIT." + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0, + "runningUserid": "testuser" + } +} \ No newline at end of file diff --git a/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.xml b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.xml new file mode 100644 index 00000000..3d033fa7 --- /dev/null +++ b/tests/common/common_result_samples/add_resource_result_precheck_uacc_none_error.xml @@ -0,0 +1,21 @@ + + + + + 8 + 16 + 4 + RDEFINE XFACILIT (IRR.IRRSMO00.PRECHECK) + ICH10103I NOT AUTHORIZED TO DEFINE IRR.IRRSMO00.PRECHECK. + + + 8 + 16 + 4 + RALTER XFACILIT (IRR.IRRSMO00.PRECHECK) UACC (None) + ICH11102I IRR.IRRSMO00.PRECHECK NOT DEFINED TO CLASS XFACILIT. + + + 4 + 0 + \ No newline at end of file diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 6761f191..946c798f 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -29,6 +29,13 @@ def get_sample(sample_file: str) -> Union[str, bytes]: "add_resource_result_precheck_uacc_none_success.json" ) +TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_ERROR_XML = get_sample( + "add_resource_result_precheck_uacc_none_error.xml" +) +TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_ERROR_DICTIONARY = get_sample( + "add_resource_result_precheck_uacc_none_error.json" +) + TEST_SETUP_PRECHECK_VALIDATED_ACCESS_TEXT = ( "'IRR.IRRSMO00.PRECHECK' is already defined, and you already have 'ALTER' access!\n" + "You are ready to start using pyRACF!\n" diff --git a/tests/common/test_setup_precheck.py b/tests/common/test_setup_precheck.py index 8e4ff9d9..a88ea605 100644 --- a/tests/common/test_setup_precheck.py +++ b/tests/common/test_setup_precheck.py @@ -9,7 +9,7 @@ import __init__ import tests.common.test_common_constants as TestCommonConstants -from pyracf import setup_precheck +from pyracf import SecurityRequestError, setup_precheck from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -43,6 +43,22 @@ def test_setup_precheck_works_when_no_setup_done( TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY, ) + def test_setup_precheck_throws_error_when_cannot_add_profile( + self, + call_racf_mock: Mock, + ): + call_racf_mock.side_effect = [ + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML, + TestCommonConstants.TEST_EXTRACT_RESOURCE_RESULT_PRECHECK_ERROR_XML, + TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_ERROR_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + setup_precheck() + self.assertEqual( + exception.exception.result, + TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_ERROR_DICTIONARY, + ) + def test_setup_precheck_works_when_alter_access_exists( self, call_racf_mock: Mock, From 2d7c512a79e769dc8fbce83abcbb675154dc6d01 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 19 Dec 2023 11:59:02 -0500 Subject: [PATCH 22/30] Update test_setup_precheck.py Signed-off-by: Elijah Swift --- tests/common/test_setup_precheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/test_setup_precheck.py b/tests/common/test_setup_precheck.py index a88ea605..b348adff 100644 --- a/tests/common/test_setup_precheck.py +++ b/tests/common/test_setup_precheck.py @@ -43,7 +43,7 @@ def test_setup_precheck_works_when_no_setup_done( TestCommonConstants.TEST_ADD_RESOURCE_PRECHECK_UACC_NONE_SUCCESS_DICTIONARY, ) - def test_setup_precheck_throws_error_when_cannot_add_profile( + def test_setup_precheck_throws_error_when_add_resource_fails( self, call_racf_mock: Mock, ): From 588449fb4912924b0b4bab5304b889081aa80439 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 19 Dec 2023 12:06:02 -0500 Subject: [PATCH 23/30] Update security_admin.py Change comment string for DownstreamFatalError Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 36960d0c..4df29f92 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -216,11 +216,11 @@ def _make_request( ) result_dictionary = result.get_result_dictionary() if result_dictionary["securityResult"]["returnCode"] >= 8: - # All return codes greater than 4 are indicative of an issue with - # IRRSMO00 that would stop a command image from being generated. + # All return codes greater than or equal to 8 are indicative of an issue + # with IRRSMO00 that would stop RACF from generating a command image. # The user should interogate the result dictionary attached to the - # SecurityRequestError as well as the return and reason codes to - # resolve the problem. + # DownstreamFatalError as well as the return and reason codes to resolve + # the problem. raise DownstreamFatalError( saf_return_code=8, racf_return_code=result_dictionary["securityResult"]["returnCode"], From 07c92cc6017f842dbd172c7186a164b9c9afde5e Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 19 Dec 2023 12:55:00 -0500 Subject: [PATCH 24/30] Change DownstreamFatalError Test Constants Signed-off-by: Elijah Swift --- tests/common/test_common_constants.py | 6 +++--- tests/common/test_downstream_fatal_error.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index 946c798f..fc83beca 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -122,7 +122,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Downstream Fatal Error # ============================================================================ -TEST_DOWNSTREAM_FATAL_PRECHECK_TEXT = ( +TEST_DOWNSTREAM_FATAL_ERROR_PRECHECK_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 16" + "\n\nCheck to see if the proper RACF permissions are in place.\n" @@ -130,7 +130,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: + "access to 'IRR.IRRSMO00.PRECHECK' in the 'XFACILIT' class." ) -TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT = ( +TEST_DOWNSTREAM_FATAL_ERROR_SURROGAT_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 200\nRACF Reason Code: 8" + "\n\nCheck to see if the proper RACF permissions are in place.\n" @@ -138,7 +138,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: + "access to 'ESWIFT.IRRSMO00' in the 'SURROGAT' class." ) -TEST_DOWNSTREAM_FATAL_GENERIC_TEXT = ( +TEST_DOWNSTREAM_FATAL_ERROR_GENERIC_TEXT = ( "(DownstreamFatalError) Security request made to IRRSMO00 failed." + "\n\nSAF Return Code: 8\nRACF Return Code: 2000\nRACF Reason Code: 20" + "\n\nPlease check the specified return and reason codes against " diff --git a/tests/common/test_downstream_fatal_error.py b/tests/common/test_downstream_fatal_error.py index 86b8f485..247e0934 100644 --- a/tests/common/test_downstream_fatal_error.py +++ b/tests/common/test_downstream_fatal_error.py @@ -28,7 +28,7 @@ def test_donwstream_fatal_error_thrown_on_precheck_error( self.user_admin.set_password("TESTUSER", "Testpass") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_DOWNSTREAM_FATAL_PRECHECK_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_ERROR_PRECHECK_TEXT, ) def test_donwstream_fatal_error_thrown_on_surrogat_error( @@ -41,7 +41,7 @@ def test_donwstream_fatal_error_thrown_on_surrogat_error( self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_DOWNSTREAM_FATAL_SURROGAT_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_ERROR_SURROGAT_TEXT, ) def test_donwstream_fatal_error_thrown_on_miscellaneous_error( @@ -53,5 +53,5 @@ def test_donwstream_fatal_error_thrown_on_miscellaneous_error( self.user_admin.add("squidwrd") self.assertEqual( exception.exception.message, - TestCommonConstants.TEST_DOWNSTREAM_FATAL_GENERIC_TEXT, + TestCommonConstants.TEST_DOWNSTREAM_FATAL_ERROR_GENERIC_TEXT, ) From f9db7fccc2be6a1abbd20d1c6f14b646b05778ad Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 19 Dec 2023 17:19:56 -0500 Subject: [PATCH 25/30] Update irrsmo00.py Function testing revealed a couple of bugs in how the response value from the c code was parsed in python. This resolves that issue. Signed-off-by: Elijah Swift --- pyracf/common/irrsmo00.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyracf/common/irrsmo00.py b/pyracf/common/irrsmo00.py index 6f9174fc..1c328841 100644 --- a/pyracf/common/irrsmo00.py +++ b/pyracf/common/irrsmo00.py @@ -40,6 +40,6 @@ def call_racf( userid=userid, userid_len=userid_length, ) - if response[0] == "": - return response[1:3] + if response[0] == b"": + return list(response[1:4]) return response[0].decode("cp1047") From 37b2214cba9c24a23d1cfd3b4529e1028c63ff50 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 20 Dec 2023 10:59:38 -0500 Subject: [PATCH 26/30] Update irrsmo00.c pass pointers to return codes rather than values. Signed-off-by: Elijah Swift --- pyracf/common/irrsmo00.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyracf/common/irrsmo00.c b/pyracf/common/irrsmo00.c index 56f7b3ef..774fc254 100644 --- a/pyracf/common/irrsmo00.c +++ b/pyracf/common/irrsmo00.c @@ -60,11 +60,11 @@ static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) IRRSMO64( work_area, alet, - saf_rc, + &saf_rc, alet, - racf_rc, + &racf_rc, alet, - racf_rsn, + &racf_rsn, num_parms, fn, opts, @@ -78,6 +78,7 @@ static PyObject* call_irrsmo00(PyObject* self, PyObject* args, PyObject *kwargs) ); null_byte_fix(rsp,rsp_len); + return Py_BuildValue("yBBB", rsp, saf_rc, racf_rc, racf_rsn); } From 0baee169483e8a5e85cf4c12191801c0565f12b2 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 20 Dec 2023 15:44:08 -0500 Subject: [PATCH 27/30] Function and Unit Test Changes Add Function Test for DownstreamFatalError (Null Response checking is based off something unit testing does not fully check) Add real error messages to tests in Add operations that surface error from initial extract. Signed-off-by: Elijah Swift --- tests/data_set/test_data_set_result_parser.py | 36 +++++++++++++++++++ tests/function_test/function_test.py | 20 ++++++++++- tests/group/test_group_result_parser.py | 4 +-- tests/resource/test_resource_result_parser.py | 4 +-- tests/user/test_user_result_parser.py | 4 +-- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/tests/data_set/test_data_set_result_parser.py b/tests/data_set/test_data_set_result_parser.py index c4417d35..5ab769bb 100644 --- a/tests/data_set/test_data_set_result_parser.py +++ b/tests/data_set/test_data_set_result_parser.py @@ -1,5 +1,6 @@ """Test data set profile result parser.""" +import copy import unittest from unittest.mock import Mock, patch @@ -65,6 +66,41 @@ def test_data_set_admin_thows_error_on_add_existing_data_set_profile( + f"'{self.data_set_admin._profile_type}' profile.", ) + def test_dataset_admin_add_surfaces_error_from_preliminary_extract( + self, + call_racf_mock: Mock, + ): + profile_name = "ESWIFT.TEST.T1136242.P3020470" + extract_data_set_xml = ( + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML + ) + extract_data_set_xml = extract_data_set_xml.replace( + ( + "ICH35003I NO RACF DESCRIPTION FOUND " + "FOR ESWIFT.TEST.T1136242.P3020470" + ), + "ICH35002I NOT AUTHORIZED TO LIST ESWIFT.TEST.T1136242.P3020470", + ) + call_racf_mock.side_effect = [ + extract_data_set_xml, + TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_XML, + ] + with self.assertRaises(SecurityRequestError) as exception: + self.data_set_admin.add( + profile_name, + traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, + ) + extract_data_set_error_json = copy.deepcopy( + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_DICTIONARY + ) + extract_data_set_error_json["securityResult"]["dataSet"]["commands"][0][ + "messages" + ] = ["ICH35002I NOT AUTHORIZED TO LIST ESWIFT.TEST.T1136242.P3020470"] + self.assertEqual( + exception.exception.result, + extract_data_set_error_json, + ) + def test_dataset_admin_avoids_error_on_add_covered_profile( self, call_racf_mock: Mock, diff --git a/tests/function_test/function_test.py b/tests/function_test/function_test.py index e23cf8fd..94660e3a 100644 --- a/tests/function_test/function_test.py +++ b/tests/function_test/function_test.py @@ -1,7 +1,7 @@ import getpass import json -from pyracf import UserAdmin +from pyracf import DownstreamFatalError, UserAdmin def main(): @@ -11,6 +11,24 @@ def main(): json_profile = json.dumps(profile, indent=2) print(f"Profile extract for userid '{userid}' was successful:\n\n{json_profile}\n") + # The following testcase is designed to test the DownstreamFatalError's null response + # trigger by attempting to run as a userid for 'NOTAUSER' which is assumed to not + # exist as a valid userid in this environment. If 'NOTAUSER' is a user, the test + # will still function correctly as long as the tester does not have at least 'UPDATE' + # access to 'NOTAUSER.IRRSMO00' in the 'SURROGAT' class. + user_admin.set_running_userid("NOTAUSER") + try: + user_admin.extract(userid) + except DownstreamFatalError as exception: + if ( + (exception.saf_return_code == 8) + and (exception.racf_return_code == 200) + and (exception.racf_reason_code == 8) + ): + print(f"DownstreamFatalError occured as intended:\n\n{exception.message}\n") + return 0 + return 1 + if __name__ == "__main__": main() diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index deaab8c4..d86a1766 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -74,7 +74,7 @@ def test_group_admin_add_surfaces_error_from_preliminary_extract( ) extract_group_error_xml = extract_group_error_xml.replace( "ICH51003I NAME NOT FOUND IN RACF DATA SET", - "NOT A REAL ERROR MESSAGE", + "ICH32004I NOT AUTHORIZED TO ISSUE LISTGRP", ) call_racf_mock.side_effect = [ extract_group_error_xml, @@ -89,7 +89,7 @@ def test_group_admin_add_surfaces_error_from_preliminary_extract( ) extract_group_error_json["securityResult"]["group"]["commands"][0][ "messages" - ] = ["NOT A REAL ERROR MESSAGE"] + ] = ["ICH32004I NOT AUTHORIZED TO ISSUE LISTGRP"] self.assertEqual( exception.exception.result, extract_group_error_json, diff --git a/tests/resource/test_resource_result_parser.py b/tests/resource/test_resource_result_parser.py index 0e09c0a5..16aeb732 100644 --- a/tests/resource/test_resource_result_parser.py +++ b/tests/resource/test_resource_result_parser.py @@ -75,7 +75,7 @@ def test_resource_admin_add_surfaces_error_from_preliminary_extract( ) extract_resource_xml = extract_resource_xml.replace( "ICH13003I TESTING NOT FOUND", - "NOT A REAL ERROR MESSAGE", + "ICH13002I NOT AUTHORIZED TO LIST TESTING", ) call_racf_mock.side_effect = [ extract_resource_xml, @@ -92,7 +92,7 @@ def test_resource_admin_add_surfaces_error_from_preliminary_extract( ) extract_resource_error_json["securityResult"]["resource"]["commands"][0][ "messages" - ] = ["NOT A REAL ERROR MESSAGE"] + ] = ["ICH13002I NOT AUTHORIZED TO LIST TESTING"] self.assertEqual( exception.exception.result, extract_resource_error_json, diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index 367bc01d..4111ce75 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -78,7 +78,7 @@ def test_user_admin_add_surfaces_error_from_preliminary_extract( ) extract_user_error_xml = extract_user_error_xml.replace( "ICH30001I UNABLE TO LOCATE USER ENTRY SQUIDWRD", - "NOT A REAL ERROR MESSAGE", + "ICH30010I NOT AUTHORIZED TO ISSUE LISTUSER", ) call_racf_mock.side_effect = [ extract_user_error_xml, @@ -92,7 +92,7 @@ def test_user_admin_add_surfaces_error_from_preliminary_extract( TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_ERROR_DICTIONARY ) extract_user_error_json["securityResult"]["user"]["commands"][0]["messages"] = [ - "NOT A REAL ERROR MESSAGE" + "ICH30010I NOT AUTHORIZED TO ISSUE LISTUSER" ] self.assertEqual( exception.exception.result, From 19c5df0968cc80d93872429e203c375792a69048 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Thu, 21 Dec 2023 11:11:02 -0500 Subject: [PATCH 28/30] Change docstrings and version number Changed docstring for DownstreamFatalError Update version number to 1.0b3 for next release Signed-off-by: Elijah Swift --- pyproject.toml | 2 +- pyracf/common/downstream_fatal_error.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 47ea1aa3..17bc8f0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ [tool.poetry] name="pyracf" - version="1.0b2" + version="1.0b3" description="Python interface to RACF using IRRSMO00 RACF Callable Service." license = "Apache-2.0" authors = [ diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index 25cc86b8..10597fd8 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -4,7 +4,7 @@ class DownstreamFatalError(Exception): """ - Raised IRRSMO00 returns with a SAF Return Code of 8, + Raised when IRRSMO00 returns with a SAF Return Code of 8, indicating that the request could not be processed. """ From af81fc78e1987c47d30e7b63162ba1524986c346 Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Tue, 26 Dec 2023 13:46:09 -0500 Subject: [PATCH 29/30] Drive userid on get_running_userid to lowercase Signed-off-by: Elijah Swift --- pyracf/common/downstream_fatal_error.py | 2 +- pyracf/common/security_admin.py | 2 ++ tests/common/test_common_constants.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyracf/common/downstream_fatal_error.py b/pyracf/common/downstream_fatal_error.py index 10597fd8..69610993 100644 --- a/pyracf/common/downstream_fatal_error.py +++ b/pyracf/common/downstream_fatal_error.py @@ -54,7 +54,7 @@ def __init__( self.message += ( "\n\nCheck to see if the proper RACF permissions are in place.\n" + "For the 'run_as_userid' feature, you must have at least 'UPDATE' " - + f"access to '{run_as_userid}.IRRSMO00' in the 'SURROGAT' class." + + f"access to '{run_as_userid.upper()}.IRRSMO00' in the 'SURROGAT' class." ) else: self.message += ( diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index 4df29f92..ed579cd1 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -97,6 +97,8 @@ def set_running_userid(self, userid: Union[str, None]) -> None: self.__running_userid = userid.upper() def get_running_userid(self) -> None: + if isinstance(self.__running_userid, str): + return self.__running_userid.lower() return self.__running_userid # ============================================================================ diff --git a/tests/common/test_common_constants.py b/tests/common/test_common_constants.py index fc83beca..d4dd266f 100644 --- a/tests/common/test_common_constants.py +++ b/tests/common/test_common_constants.py @@ -101,7 +101,7 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Run As UserId # ============================================================================ -TEST_RUNNING_USERID = "ESWIFT" +TEST_RUNNING_USERID = "eswift" TEST_ALTER_USER_REQUEST_TRAITS = { "base:special": False, "omvs:home_directory": "/u/clarinet", From f4171c26432354b9525d95595d4d0e69f045b11e Mon Sep 17 00:00:00 2001 From: Elijah Swift Date: Wed, 3 Jan 2024 15:34:54 -0500 Subject: [PATCH 30/30] Update security_admin.py Change type hints for get_running_userid to Union[str, None] Signed-off-by: Elijah Swift --- pyracf/common/security_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyracf/common/security_admin.py b/pyracf/common/security_admin.py index ed579cd1..6a87e5d8 100644 --- a/pyracf/common/security_admin.py +++ b/pyracf/common/security_admin.py @@ -96,7 +96,7 @@ def set_running_userid(self, userid: Union[str, None]) -> None: raise UserIdError(userid) self.__running_userid = userid.upper() - def get_running_userid(self) -> None: + def get_running_userid(self) -> Union[str, None]: if isinstance(self.__running_userid, str): return self.__running_userid.lower() return self.__running_userid