diff --git a/pyracf/data_set/data_set_admin.py b/pyracf/data_set/data_set_admin.py index 359d3167..1610617e 100644 --- a/pyracf/data_set/data_set_admin.py +++ b/pyracf/data_set/data_set_admin.py @@ -2,6 +2,7 @@ from typing import List, Union +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 @@ -99,15 +100,26 @@ def get_my_access(self, data_set: str) -> Union[str, bytes, None]: def add( self, data_set: str, - traits: dict, + traits: dict = {}, volume: Union[str, None] = None, generic: bool = False, ) -> Union[dict, bytes]: """Create a new data set profile.""" - self._build_segment_dictionaries(traits) - data_set_request = DataSetRequest(data_set, "set", volume, generic) - self._build_xml_segments(data_set_request) - return self._make_request(data_set_request) + if self._generate_requests_only: + self._build_segment_dictionaries(traits) + data_set_request = DataSetRequest(data_set, "set", volume, generic) + self._build_xml_segments(data_set_request) + return self._make_request(data_set_request) + try: + self.extract(data_set, volume=volume, generic=generic) + except SecurityRequestError as exception: + if not exception.scan_for_error("dataSet", "ICH35003I"): + raise exception + self._build_segment_dictionaries(traits) + data_set_request = DataSetRequest(data_set, "set", volume, generic) + self._build_xml_segments(data_set_request) + return self._make_request(data_set_request) + raise AddOperationError(data_set, "DATASET") def alter( self, diff --git a/pyracf/group/group_admin.py b/pyracf/group/group_admin.py index 58a5f597..fde464f3 100644 --- a/pyracf/group/group_admin.py +++ b/pyracf/group/group_admin.py @@ -2,6 +2,7 @@ from typing import List, Union +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 @@ -121,12 +122,23 @@ def set_ovm_gid(self, group: str, gid: int) -> Union[dict, bytes]: # ============================================================================ def add(self, group: str, traits: dict = {}) -> Union[dict, bytes]: """Create a new group.""" - self._build_segment_dictionaries(traits) - group_request = GroupRequest(group, "set") - self._build_xml_segments(group_request) - return self._make_request(group_request) - - def alter(self, group: str, traits: dict = {}) -> Union[dict, bytes]: + if self._generate_requests_only: + self._build_segment_dictionaries(traits) + group_request = GroupRequest(group, "set") + self._build_xml_segments(group_request) + return self._make_request(group_request) + try: + self.extract(group) + except SecurityRequestError as exception: + if not exception.scan_for_error("group", "ICH51003I"): + raise exception + self._build_segment_dictionaries(traits) + group_request = GroupRequest(group, "set") + self._build_xml_segments(group_request) + return self._make_request(group_request) + raise AddOperationError(group, "GROUP") + + def alter(self, group: str, traits: dict) -> Union[dict, bytes]: """Alter an existing group.""" try: self.extract(group) diff --git a/pyracf/resource/resource_admin.py b/pyracf/resource/resource_admin.py index 0d9eb7a5..59c388da 100644 --- a/pyracf/resource/resource_admin.py +++ b/pyracf/resource/resource_admin.py @@ -2,6 +2,7 @@ from typing import List, Union +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 @@ -222,14 +223,23 @@ def add( self, resource: str, class_name: str, traits: dict = {} ) -> Union[dict, bytes]: """Create a new general resource profile.""" - self._build_segment_dictionaries(traits) - profile_request = ResourceRequest(resource, class_name, "set") - self._build_xml_segments(profile_request) - return self._make_request(profile_request) + if self._generate_requests_only: + self._build_segment_dictionaries(traits) + profile_request = ResourceRequest(resource, class_name, "set") + self._build_xml_segments(profile_request) + return self._make_request(profile_request) + try: + self.extract(resource, class_name) + except SecurityRequestError as exception: + if not exception.scan_for_error("resource", "ICH13003I"): + raise exception + self._build_segment_dictionaries(traits) + profile_request = ResourceRequest(resource, class_name, "set") + self._build_xml_segments(profile_request) + return self._make_request(profile_request) + raise AddOperationError(resource, class_name) - def alter( - self, resource: str, class_name: str, traits: dict = {} - ) -> Union[dict, bytes]: + def alter(self, resource: str, class_name: str, traits: dict) -> Union[dict, bytes]: """Alter an existing general resource profile.""" try: self.extract(resource, class_name) diff --git a/pyracf/user/user_admin.py b/pyracf/user/user_admin.py index ff806e17..8db20d69 100644 --- a/pyracf/user/user_admin.py +++ b/pyracf/user/user_admin.py @@ -784,7 +784,7 @@ def add(self, userid: str, traits: dict = {}) -> Union[dict, bytes]: return self._make_request(user_request) raise AddOperationError(userid, "USER") - def alter(self, userid: str, traits: dict = {}) -> Union[dict, bytes]: + def alter(self, userid: str, traits: dict) -> Union[dict, bytes]: """Alter an existing user.""" if self._generate_requests_only: self._build_segment_dictionaries(traits) diff --git a/tests/data_set/data_set_log_samples/add_data_set_error.log b/tests/data_set/data_set_log_samples/add_data_set_error.log deleted file mode 100644 index 24b52aed..00000000 --- a/tests/data_set/data_set_log_samples/add_data_set_error.log +++ /dev/null @@ -1,101 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - DataSetAdmin.add() - - -{ - "base": { - "base:universal_access": { - "value": "None", - "operation": null - }, - "base:owner": { - "value": "eswift", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - DataSetAdmin.add() - - - - - - None - eswift - - - - - - [pyRACF:Debug] - Result XML - DataSetAdmin.add() - - - - - - - 8 - 16 - 4 - ADDSD ('ESWIFF.TEST.T1136242.P3020470') - ICH09006I USER OR GROUP ESWIFF NOT DEFINED TO RACF - - - 8 - 16 - 4 - ALTDSD ('ESWIFF.TEST.T1136242.P3020470') UACC (None) OWNER (eswift) - ICH22001I ESWIFF.TEST.T1136242.P3020470 NOT DEFINED TO RACF - - - 4 - 0 - - - - [pyRACF:Debug] - Result Dictionary - DataSetAdmin.add() - - -{ - "securityResult": { - "dataSet": { - "name": "ESWIFF.TEST.T1136242.P3020470", - "operation": "set", - "generic": "no", - "requestId": "DatasetRequest", - "commands": [ - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 4, - "image": "ADDSD ('ESWIFF.TEST.T1136242.P3020470')", - "messages": [ - "ICH09006I USER OR GROUP ESWIFF NOT DEFINED TO RACF" - ] - }, - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 4, - "image": "ALTDSD ('ESWIFF.TEST.T1136242.P3020470') UACC (None) OWNER (eswift)", - "messages": [ - "ICH22001I ESWIFF.TEST.T1136242.P3020470 NOT DEFINED TO RACF" - ] - } - ] - }, - "returnCode": 4, - "reasonCode": 0 - } -} - diff --git a/tests/data_set/data_set_log_samples/add_data_set_success.log b/tests/data_set/data_set_log_samples/add_data_set_success.log deleted file mode 100644 index 47501b50..00000000 --- a/tests/data_set/data_set_log_samples/add_data_set_success.log +++ /dev/null @@ -1,93 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - DataSetAdmin.add() - - -{ - "base": { - "base:universal_access": { - "value": "None", - "operation": null - }, - "base:owner": { - "value": "eswift", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - DataSetAdmin.add() - - - - - - None - eswift - - - - - - [pyRACF:Debug] - Result XML - DataSetAdmin.add() - - - - - - - 0 - 0 - 0 - ADDSD ('ESWIFT.TEST.T1136242.P3020470') - - - 0 - 0 - 0 - ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (None) OWNER (eswift) - - - 0 - 0 - - - - [pyRACF:Debug] - Result Dictionary - DataSetAdmin.add() - - -{ - "securityResult": { - "dataSet": { - "name": "ESWIFT.TEST.T1136242.P3020470", - "operation": "set", - "generic": "no", - "requestId": "DatasetRequest", - "commands": [ - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ADDSD ('ESWIFT.TEST.T1136242.P3020470')" - }, - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (None) OWNER (eswift)" - } - ] - }, - "returnCode": 0, - "reasonCode": 0 - } -} - 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 new file mode 100644 index 00000000..e637e65f --- /dev/null +++ b/tests/data_set/data_set_log_samples/alter_data_set_error.log @@ -0,0 +1,265 @@ + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470') + INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470 + + LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE + ----- -------- ---------------- ------- ----- + 00 ESWIFT READ NO NO + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + -------- + NO USER TO BE NOTIFIED + + YOUR ACCESS CREATION GROUP DATASET TYPE + ----------- -------------- ------------ + ALTER SYS1 NON-VSAM + + VOLUMES ON WHICH DATASET RESIDES + -------------------------------- + USRAT2 + + NO INSTALLATION DATA + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "messages": [ + "INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470", + null, + "LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE", + "----- -------- ---------------- ------- -----", + " 00 ESWIFT READ NO NO", + null, + "AUDITING", + "--------", + "FAILURES(READ)", + null, + "NOTIFY", + "--------", + "NO USER TO BE NOTIFIED", + null, + "YOUR ACCESS CREATION GROUP DATASET TYPE", + "----------- -------------- ------------", + " ALTER SYS1 NON-VSAM", + null, + "VOLUMES ON WHICH DATASET RESIDES", + "--------------------------------", + "USRAT2", + null, + "NO INSTALLATION DATA" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "profiles": [ + { + "base": { + "name": "eswift.test.t1136242.p3020470", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "warning": null, + "erase": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "yourAccess": "alter", + "creationGroup": "sys1", + "dataSetType": "non-vsam", + "volumes": [ + "usrat2" + ], + "installationData": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "Read", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + Read + eswift + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 8 + 16 + 4 + ADDSD ('ESWIFT.TEST.T113622.P3020470') + ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG + + + 8 + 16 + 4 + ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (ALTER) + ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T113622.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ADDSD ('ESWIFT.TEST.T113622.P3020470')", + "messages": [ + "ICH09005I ESWIFT.TEST.T113622.P3020470 NOT FOUND IN CATALOG" + ] + }, + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "ALTDSD ('ESWIFT.TEST.T113622.P3020470') UACC (ALTER)", + "messages": [ + "ICH22001I ESWIFT.TEST.T113622.P3020470 NOT DEFINED TO RACF" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} + 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 new file mode 100644 index 00000000..131978a6 --- /dev/null +++ b/tests/data_set/data_set_log_samples/alter_data_set_success.log @@ -0,0 +1,249 @@ + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470') + INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470 + + LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE + ----- -------- ---------------- ------- ----- + 00 ESWIFT READ NO NO + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + -------- + NO USER TO BE NOTIFIED + + YOUR ACCESS CREATION GROUP DATASET TYPE + ----------- -------------- ------------ + ALTER SYS1 NON-VSAM + + VOLUMES ON WHICH DATASET RESIDES + -------------------------------- + USRAT2 + + NO INSTALLATION DATA + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "messages": [ + "INFORMATION FOR DATASET ESWIFT.TEST.T1136242.P3020470", + null, + "LEVEL OWNER UNIVERSAL ACCESS WARNING ERASE", + "----- -------- ---------------- ------- -----", + " 00 ESWIFT READ NO NO", + null, + "AUDITING", + "--------", + "FAILURES(READ)", + null, + "NOTIFY", + "--------", + "NO USER TO BE NOTIFIED", + null, + "YOUR ACCESS CREATION GROUP DATASET TYPE", + "----------- -------------- ------------", + " ALTER SYS1 NON-VSAM", + null, + "VOLUMES ON WHICH DATASET RESIDES", + "--------------------------------", + "USRAT2", + null, + "NO INSTALLATION DATA" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTDSD DATASET ('ESWIFT.TEST.T1136242.P3020470')", + "profiles": [ + { + "base": { + "name": "eswift.test.t1136242.p3020470", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "warning": null, + "erase": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "yourAccess": "alter", + "creationGroup": "sys1", + "dataSetType": "non-vsam", + "volumes": [ + "usrat2" + ], + "installationData": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + DataSetAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "Read", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + DataSetAdmin.alter() + + + + + + Read + eswift + + + + + + [pyRACF:Debug] + Result XML + DataSetAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (eswift) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + DataSetAdmin.alter() + + +{ + "securityResult": { + "dataSet": { + "name": "ESWIFT.TEST.T1136242.P3020470", + "operation": "set", + "generic": "no", + "requestId": "DatasetRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTDSD ('ESWIFT.TEST.T1136242.P3020470') UACC (Read) OWNER (eswift)" + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + diff --git a/tests/data_set/data_set_log_samples/extract_data_set_base_error.log b/tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log similarity index 100% rename from tests/data_set/data_set_log_samples/extract_data_set_base_error.log rename to tests/data_set/data_set_log_samples/extract_data_set_base_only_error.log diff --git a/tests/data_set/data_set_log_samples/extract_data_set_base_success.log b/tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log similarity index 100% rename from tests/data_set/data_set_log_samples/extract_data_set_base_success.log rename to tests/data_set/data_set_log_samples/extract_data_set_base_only_success.log 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 263aefe9..ed2fc26a 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 @@ -1,7 +1,7 @@ { "securityResult": { "dataSet": { - "name": "ESWIFF.TEST.T1136242.P3020470", + "name": "ESWIFTTESTT1136242P3020470", "operation": "set", "generic": "no", "requestId": "DatasetRequest", @@ -9,19 +9,10 @@ { "safReturnCode": 8, "returnCode": 16, - "reasonCode": 4, - "image": "ADDSD ('ESWIFF.TEST.T1136242.P3020470')", + "reasonCode": 8, + "image": "ADDSD ('ESWIFTTESTT1136242P3020470') ", "messages": [ - "ICH09006I USER OR GROUP ESWIFF NOT DEFINED TO RACF" - ] - }, - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 4, - "image": "ALTDSD ('ESWIFF.TEST.T1136242.P3020470') UACC (None) OWNER (eswift)", - "messages": [ - "ICH22001I ESWIFF.TEST.T1136242.P3020470 NOT DEFINED TO RACF" + "IKJ56702I INVALID DATASET NAME, 'ESWIFTTESTT1136242P3020470'" ] } ] diff --git a/tests/data_set/data_set_result_samples/add_data_set_result_error.xml b/tests/data_set/data_set_result_samples/add_data_set_result_error.xml index dd9ba686..2b86b12a 100644 --- a/tests/data_set/data_set_result_samples/add_data_set_result_error.xml +++ b/tests/data_set/data_set_result_samples/add_data_set_result_error.xml @@ -1,19 +1,12 @@ - + 8 16 - 4 - ADDSD ('ESWIFF.TEST.T1136242.P3020470') - ICH09006I USER OR GROUP ESWIFF NOT DEFINED TO RACF - - - 8 - 16 - 4 - ALTDSD ('ESWIFF.TEST.T1136242.P3020470') UACC (None) OWNER (eswift) - ICH22001I ESWIFF.TEST.T1136242.P3020470 NOT DEFINED TO RACF + 8 + ADDSD ('ESWIFFTESTT1136242P3020470') + IKJ56702I INVALID DATASET NAME, 'ESWIFFTESTT1136242P3020470' 4 diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_error.json b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_base_error.json rename to tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.json diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_error.xml b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.xml similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_base_error.xml rename to tests/data_set/data_set_result_samples/extract_data_set_result_base_only_error.xml diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_success.json b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_base_success.json rename to tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.json diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_base_success.xml b/tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.xml similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_base_success.xml rename to tests/data_set/data_set_result_samples/extract_data_set_result_base_only_success.xml diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_success.json b/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_success.json rename to tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.json diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_success.xml b/tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.xml similarity index 100% rename from tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_success.xml rename to tests/data_set/data_set_result_samples/extract_data_set_result_generic_base_only_success.xml diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.json b/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.json new file mode 100644 index 00000000..b5d84c93 --- /dev/null +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.json @@ -0,0 +1,25 @@ +{ + "securityResult": { + "dataSet": { + "name": "ESWIFTTESTT1136242P3020470", + "operation": "listdata", + "generic": "no", + "requestId": "DatasetRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "LISTDSD DATASET ('ESWIFTTESTT1136242P3020470') ", + "messages": [ + "IKJ56702I INVALID DATASET NAME, 'ESWIFTTESTT1136242P3020470'", + "IKJ56701I MISSING DATASET NAME+", + "IKJ56701I MISSING DATASET NAMES FOR WHICH YOU WANT RACF INFORMATION LISTED" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} \ No newline at end of file diff --git a/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.xml b/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.xml new file mode 100644 index 00000000..3a0a02de --- /dev/null +++ b/tests/data_set/data_set_result_samples/extract_data_set_result_invalid_attribute_error.xml @@ -0,0 +1,16 @@ + + + + + 8 + 16 + 8 + LISTDSD DATASET ('ESWIFTTESTT1136242P3020470') + IKJ56702I INVALID DATASET NAME, 'ESWIFTTESTT1136242P3020470' + IKJ56701I MISSING DATASET NAME+ + IKJ56701I MISSING DATASET NAMES FOR WHICH YOU WANT RACF INFORMATION LISTED + + + 4 + 0 + \ No newline at end of file diff --git a/tests/data_set/test_data_set_constants.py b/tests/data_set/test_data_set_constants.py index b4801ded..d68a090a 100644 --- a/tests/data_set/test_data_set_constants.py +++ b/tests/data_set/test_data_set_constants.py @@ -40,23 +40,29 @@ def get_sample(sample_file: str) -> Union[str, bytes]: ) # Extract Data Set -TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML = get_sample( - "extract_data_set_result_base_success.xml" +TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML = get_sample( + "extract_data_set_result_base_only_success.xml" ) -TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_DICTIONARY = get_sample( - "extract_data_set_result_base_success.json" +TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_DICTIONARY = get_sample( + "extract_data_set_result_base_only_success.json" ) -TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_SUCCESS_XML = get_sample( - "extract_data_set_result_generic_base_success.xml" +TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_ONLY_SUCCESS_XML = get_sample( + "extract_data_set_result_generic_base_only_success.xml" ) -TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_SUCCESS_DICTIONARY = get_sample( - "extract_data_set_result_generic_base_success.json" +TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_ONLY_SUCCESS_DICTIONARY = get_sample( + "extract_data_set_result_generic_base_only_success.json" ) -TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML = get_sample( - "extract_data_set_result_base_error.xml" +TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML = get_sample( + "extract_data_set_result_base_only_error.xml" ) -TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_DICTIONARY = get_sample( - "extract_data_set_result_base_error.json" +TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_DICTIONARY = get_sample( + "extract_data_set_result_base_only_error.json" +) +TEST_EXTRACT_DATA_SET_RESULT_INVALID_ATTRIBUTE_ERROR_XML = get_sample( + "extract_data_set_result_invalid_attribute_error.xml" +) +TEST_EXTRACT_DATA_SET_RESULT_INVALID_ATTRIBUTE_ERROR_DICTIONARY = get_sample( + "extract_data_set_result_invalid_attribute_error.json" ) # Delete Data Set @@ -118,8 +124,12 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Debug Logging # ============================================================================ -TEST_ADD_DATA_SET_SUCCESS_LOG = get_sample("add_data_set_success.log") -TEST_ADD_DATA_SET_ERROR_LOG = get_sample("add_data_set_error.log") +TEST_ALTER_DATA_SET_SUCCESS_LOG = get_sample("alter_data_set_success.log") +TEST_ALTER_DATA_SET_ERROR_LOG = get_sample("alter_data_set_error.log") -TEST_EXTRACT_DATA_SET_BASE_SUCCESS_LOG = get_sample("extract_data_set_base_success.log") -TEST_EXTRACT_DATA_SET_BASE_ERROR_LOG = get_sample("extract_data_set_base_error.log") +TEST_EXTRACT_DATA_SET_BASE_ONLY_SUCCESS_LOG = get_sample( + "extract_data_set_base_only_success.log" +) +TEST_EXTRACT_DATA_SET_BASE_ONLY_ERROR_LOG = get_sample( + "extract_data_set_base_only_error.log" +) diff --git a/tests/data_set/test_data_set_debug_logging.py b/tests/data_set/test_data_set_debug_logging.py index e5fa2a9d..01a13452 100644 --- a/tests/data_set/test_data_set_debug_logging.py +++ b/tests/data_set/test_data_set_debug_logging.py @@ -24,44 +24,46 @@ class TestDataSetDebugLogging(unittest.TestCase): ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") # ============================================================================ - # Add Data Set + # Alter Data Set # ============================================================================ - def test_add_data_set_request_debug_log_works_on_success( + def test_alter_data_set_request_debug_log_works_on_success( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - self.data_set_admin.add( + self.data_set_admin.alter( "ESWIFT.TEST.T1136242.P3020470", - traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, ) success_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - success_log, TestDataSetConstants.TEST_ADD_DATA_SET_SUCCESS_LOG + success_log, TestDataSetConstants.TEST_ALTER_DATA_SET_SUCCESS_LOG ) - def test_add_data_set_request_debug_log_works_on_error( + def test_alter_data_set_request_debug_log_works_on_error( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_XML - ) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_ERROR_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): try: - self.data_set_admin.add( - "ESWIFF.TEST.T1136242.P3020470", - traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, + self.data_set_admin.alter( + "ESWIFT.TEST.T1136242.P3020470", + traits=TestDataSetConstants.TEST_ALTER_DATA_SET_REQUEST_TRAITS, ) except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual(error_log, TestDataSetConstants.TEST_ADD_DATA_SET_ERROR_LOG) + self.assertEqual(error_log, TestDataSetConstants.TEST_ALTER_DATA_SET_ERROR_LOG) # ============================================================================ # Extract Data Set @@ -71,14 +73,15 @@ def test_extract_data_set_base_request_debug_log_works_on_success( call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") success_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - success_log, TestDataSetConstants.TEST_EXTRACT_DATA_SET_BASE_SUCCESS_LOG + success_log, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_BASE_ONLY_SUCCESS_LOG, ) def test_extract_data_set_base_request_debug_log_works_on_error( @@ -86,7 +89,7 @@ def test_extract_data_set_base_request_debug_log_works_on_error( call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) stdout = io.StringIO() with contextlib.redirect_stdout(stdout): @@ -96,5 +99,5 @@ def test_extract_data_set_base_request_debug_log_works_on_error( pass error_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - error_log, TestDataSetConstants.TEST_EXTRACT_DATA_SET_BASE_ERROR_LOG + error_log, TestDataSetConstants.TEST_EXTRACT_DATA_SET_BASE_ONLY_ERROR_LOG ) diff --git a/tests/data_set/test_data_set_getters.py b/tests/data_set/test_data_set_getters.py index 8e630eb5..0437b283 100644 --- a/tests/data_set/test_data_set_getters.py +++ b/tests/data_set/test_data_set_getters.py @@ -27,7 +27,7 @@ def test_data_set_admin_get_universal_access_returns_valid_when_read( call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertEqual( self.data_set_admin.get_universal_access("ESWIFT.TEST.T1136242.P3020470"), @@ -39,7 +39,7 @@ def test_data_set_admin_get_universal_access_returns_valid_when_none( call_racf_mock: Mock, ): data_set_extract_no_universal_access = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) data_set_extract_no_universal_access = ( data_set_extract_no_universal_access.replace( @@ -58,7 +58,7 @@ def test_data_set_admin_get_universal_access_raises_an_exception_when_extract_fa call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) with self.assertRaises(SecurityRequestError): self.data_set_admin.get_universal_access("ESWIFT.TEST.T1136242.P3020470") @@ -68,7 +68,7 @@ def test_data_set_admin_get_my_access_returns_valid_when_alter( call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertEqual( self.data_set_admin.get_my_access("ESWIFT.TEST.T1136242.P3020470"), "alter" @@ -79,7 +79,7 @@ def test_data_set_admin_get_my_access_returns_valid_when_none( call_racf_mock: Mock, ): data_set_extract_no_my_access = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) data_set_extract_no_my_access = data_set_extract_no_my_access.replace( " ALTER SYS1 NON-VSAM", @@ -96,7 +96,7 @@ def test_data_set_admin_get_my_access_raises_an_exception_when_extract_fails( call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) 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 1dae2aa3..48aaca27 100644 --- a/tests/data_set/test_data_set_result_parser.py +++ b/tests/data_set/test_data_set_result_parser.py @@ -6,7 +6,12 @@ import __init__ import tests.data_set.test_data_set_constants as TestDataSetConstants -from pyracf import AlterOperationError, DataSetAdmin, SecurityRequestError +from pyracf import ( + AddOperationError, + AlterOperationError, + DataSetAdmin, + SecurityRequestError, +) from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -26,9 +31,10 @@ def test_data_set_admin_can_parse_add_data_set_success_xml( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML, + TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_XML, + ] self.assertEqual( self.data_set_admin.add( "ESWIFT.TEST.T1136242.P3020470", @@ -37,22 +43,45 @@ def test_data_set_admin_can_parse_add_data_set_success_xml( TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_DICTIONARY, ) - # Error User or Group ESWIFF not defined to RACF - def test_data_set_admin_can_parse_add_data_set_error_xml( + def test_data_set_admin_thows_error_on_add_existing_data_set_profile( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_XML + profile_name = "ESWIFT.TEST.T1136242.P3020470" + class_name = "DATASET" + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, + TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_SUCCESS_XML, + ] + with self.assertRaises(AddOperationError) as exception: + self.data_set_admin.add( + profile_name, + traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, + ) + self.assertEqual( + exception.exception.message, + "Security request made to IRRSMO00 failed." + + "\n\nTarget profile " + + f"'{profile_name}' already exists as a {class_name} profile.", ) + + # Error in command, ESWIFTTESTT1136242P3020470 is not a valid DATASET + def test_data_set_admin_can_parse_add_data_set_error_xml( + self, + call_racf_mock: Mock, + ): + call_racf_mock.side_effect = [ + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_INVALID_ATTRIBUTE_ERROR_XML, + TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_XML, + ] with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.add( - "ESWIFF.TEST.T1136242.P3020470", + "ESWIFTTESTT1136242P3020470", traits=TestDataSetConstants.TEST_ADD_DATA_SET_REQUEST_TRAITS, ) self.assertEqual( exception.exception.result, - TestDataSetConstants.TEST_ADD_DATA_SET_RESULT_ERROR_DICTIONARY, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_INVALID_ATTRIBUTE_ERROR_DICTIONARY, ) # ============================================================================ @@ -63,7 +92,7 @@ def test_data_set_admin_can_parse_alter_data_set_success_xml( call_racf_mock: Mock, ): call_racf_mock.side_effect = [ - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML, TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_XML, ] self.assertEqual( @@ -81,7 +110,7 @@ def test_data_set_admin_thows_error_on_alter_new_data_set_profile( profile_name = "ESWIFT.TEST.T1136242.P3020470" class_name = "DATASET" call_racf_mock.side_effect = [ - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML, TestDataSetConstants.TEST_ALTER_DATA_SET_RESULT_SUCCESS_XML, ] with self.assertRaises(AlterOperationError) as exception: @@ -102,7 +131,7 @@ def test_data_set_admin_can_parse_alter_data_set_error_xml( call_racf_mock: Mock, ): call_racf_mock.side_effect = [ - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_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: @@ -118,43 +147,43 @@ def test_data_set_admin_can_parse_alter_data_set_error_xml( # ============================================================================ # Extract Data Set # ============================================================================ - def test_data_set_admin_can_parse_extract_data_set_base_success_xml( + def test_data_set_admin_can_parse_extract_data_set_base_only_success_xml( self, call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_XML ) self.assertEqual( self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470"), - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_SUCCESS_DICTIONARY, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_SUCCESS_DICTIONARY, ) - def test_data_set_admin_can_parse_extract_data_set_generic_base_success_xml( + def test_data_set_admin_can_parse_extract_data_set_generic_base_only_success_xml( self, call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_SUCCESS_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_ONLY_SUCCESS_XML ) self.assertEqual( self.data_set_admin.extract("ESWIFT.TEST.T1136242.*"), - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_SUCCESS_DICTIONARY, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_GENERIC_BASE_ONLY_SUCCESS_DICTIONARY, ) # Error in environment, ESWIFT.TEST.T1136242.P3020470 already deleted/not added - def test_data_set_admin_can_parse_extract_data_set_base_error_xml( + def test_data_set_admin_can_parse_extract_data_set_base_only_error_xml( self, call_racf_mock: Mock, ): call_racf_mock.return_value = ( - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_XML + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_XML ) with self.assertRaises(SecurityRequestError) as exception: self.data_set_admin.extract("ESWIFT.TEST.T1136242.P3020470") self.assertEqual( exception.exception.result, - TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ERROR_DICTIONARY, + TestDataSetConstants.TEST_EXTRACT_DATA_SET_RESULT_BASE_ONLY_ERROR_DICTIONARY, ) # ============================================================================ diff --git a/tests/group/group_log_samples/add_group_error.log b/tests/group/group_log_samples/add_group_error.log deleted file mode 100644 index 8b14c1a2..00000000 --- a/tests/group/group_log_samples/add_group_error.log +++ /dev/null @@ -1,91 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - GroupAdmin.add() - - -{ - "omvs": { - "omvs:gid": { - "value": "6667", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - GroupAdmin.add() - - - - - - 6667 - - - - - - [pyRACF:Debug] - Result XML - GroupAdmin.add() - - - - - - - 8 - 16 - 8 - ADDGROUP TESTGRP0 - IKJ56702I INVALID GROUP, TESTGRP0 - - - 0 - 0 - 0 - ALTGROUP TESTGRP0 OMVS (GID (6667)) - - - 4 - 0 - - - - [pyRACF:Debug] - Result Dictionary - GroupAdmin.add() - - -{ - "securityResult": { - "group": { - "name": "TESTGRP0", - "operation": "set", - "requestId": "GroupRequest", - "commands": [ - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 8, - "image": "ADDGROUP TESTGRP0 ", - "messages": [ - "IKJ56702I INVALID GROUP, TESTGRP0" - ] - }, - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ALTGROUP TESTGRP0 OMVS (GID (6667))" - } - ] - }, - "returnCode": 4, - "reasonCode": 0 - } -} - diff --git a/tests/group/group_log_samples/add_group_success.log b/tests/group/group_log_samples/add_group_success.log deleted file mode 100644 index 3f0c67d4..00000000 --- a/tests/group/group_log_samples/add_group_success.log +++ /dev/null @@ -1,87 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - GroupAdmin.add() - - -{ - "omvs": { - "omvs:gid": { - "value": "6667", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - GroupAdmin.add() - - - - - - 6667 - - - - - - [pyRACF:Debug] - Result XML - GroupAdmin.add() - - - - - - - 0 - 0 - 0 - ADDGROUP TESTGRP0 - - - 0 - 0 - 0 - ALTGROUP TESTGRP0 OMVS (GID (6667)) - - - 0 - 0 - - - - [pyRACF:Debug] - Result Dictionary - GroupAdmin.add() - - -{ - "securityResult": { - "group": { - "name": "TESTGRP0", - "operation": "set", - "requestId": "GroupRequest", - "commands": [ - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ADDGROUP TESTGRP0 " - }, - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ALTGROUP TESTGRP0 OMVS (GID (6667))" - } - ] - }, - "returnCode": 0, - "reasonCode": 0 - } -} - diff --git a/tests/group/group_log_samples/alter_group_error.log b/tests/group/group_log_samples/alter_group_error.log new file mode 100644 index 00000000..8b1ad45c --- /dev/null +++ b/tests/group/group_log_samples/alter_group_error.log @@ -0,0 +1,243 @@ + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTGRP TESTGRP0 + INFORMATION FOR GROUP TESTGRP0 + SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + NO SUBGROUPS + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + ESWIFT USE 000000 NONE + CONNECT ATTRIBUTES=SPECIAL + REVOKE DATE=NONE RESUME DATE=NONE + LEONARD USE 000000 NONE + CONNECT ATTRIBUTES=OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "messages": [ + "INFORMATION FOR GROUP TESTGRP0", + " SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150", + " NO INSTALLATION DATA", + " NO MODEL DATA SET", + " TERMUACC", + " NO SUBGROUPS", + " USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=", + " ESWIFT USE 000000 NONE", + " CONNECT ATTRIBUTES=SPECIAL", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LEONARD USE 000000 NONE", + " CONNECT ATTRIBUTES=OPERATIONS", + " REVOKE DATE=NONE RESUME DATE=NONE" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "profiles": [ + { + "base": { + "name": "testgrp0", + "superiorGroup": "sys1", + "owner": "eswift", + "created": "5/30/2023", + "installationData": null, + "modelDataSet": null, + "terminalUniversalAccess": true, + "subgroups": [], + "users": [ + { + "userid": "eswift", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "special" + ], + "revokeDate": null, + "resumeDate": null + }, + { + "userid": "leonard", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "operations" + ], + "revokeDate": null, + "resumeDate": null + } + ] + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{ + "omvs": { + "omvs:gid": { + "value": "3000000000", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + 3000000000 + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 8 + 16 + 8 + ALTGROUP TESTGRP0 OMVS (GID (3000000000)) + IKJ56702I INVALID GID, 3000000000 + IKJ56701I MISSING OMVS GID+ + IKJ56701I MISSING OMVS GROUP ID (GID), 1-10 NUMERIC DIGITS + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "ALTGROUP TESTGRP0 OMVS (GID (3000000000))", + "messages": [ + "IKJ56702I INVALID GID, 3000000000", + "IKJ56701I MISSING OMVS GID+", + "IKJ56701I MISSING OMVS GROUP ID (GID), 1-10 NUMERIC DIGITS" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} + diff --git a/tests/group/group_log_samples/alter_group_success.log b/tests/group/group_log_samples/alter_group_success.log new file mode 100644 index 00000000..1e1d1365 --- /dev/null +++ b/tests/group/group_log_samples/alter_group_success.log @@ -0,0 +1,235 @@ + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + + 0 + 0 + 0 + LISTGRP TESTGRP0 + INFORMATION FOR GROUP TESTGRP0 + SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150 + NO INSTALLATION DATA + NO MODEL DATA SET + TERMUACC + NO SUBGROUPS + USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS= + ESWIFT USE 000000 NONE + CONNECT ATTRIBUTES=SPECIAL + REVOKE DATE=NONE RESUME DATE=NONE + LEONARD USE 000000 NONE + CONNECT ATTRIBUTES=OPERATIONS + REVOKE DATE=NONE RESUME DATE=NONE + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "messages": [ + "INFORMATION FOR GROUP TESTGRP0", + " SUPERIOR GROUP=SYS1 OWNER=ESWIFT CREATED=23.150", + " NO INSTALLATION DATA", + " NO MODEL DATA SET", + " TERMUACC", + " NO SUBGROUPS", + " USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=", + " ESWIFT USE 000000 NONE", + " CONNECT ATTRIBUTES=SPECIAL", + " REVOKE DATE=NONE RESUME DATE=NONE", + " LEONARD USE 000000 NONE", + " CONNECT ATTRIBUTES=OPERATIONS", + " REVOKE DATE=NONE RESUME DATE=NONE" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "LISTGRP TESTGRP0 ", + "profiles": [ + { + "base": { + "name": "testgrp0", + "superiorGroup": "sys1", + "owner": "eswift", + "created": "5/30/2023", + "installationData": null, + "modelDataSet": null, + "terminalUniversalAccess": true, + "subgroups": [], + "users": [ + { + "userid": "eswift", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "special" + ], + "revokeDate": null, + "resumeDate": null + }, + { + "userid": "leonard", + "access": "use", + "accessCount": 0, + "universalAccess": null, + "connectAttributes": [ + "operations" + ], + "revokeDate": null, + "resumeDate": null + } + ] + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + GroupAdmin.alter() + + +{ + "omvs": { + "omvs:gid": { + "value": "1234567", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + GroupAdmin.alter() + + + + + + 1234567 + + + + + + [pyRACF:Debug] + Result XML + GroupAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + ALTGROUP TESTGRP0 OMVS (GID (1234567)) + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + GroupAdmin.alter() + + +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "set", + "requestId": "GroupRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "ALTGROUP TESTGRP0 OMVS (GID (1234567))" + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + 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 11d6df12..4df6b394 100644 --- a/tests/group/group_result_samples/add_group_result_error.json +++ b/tests/group/group_result_samples/add_group_result_error.json @@ -1,28 +1,19 @@ { "securityResult": { "group": { - "name": "TESTGRP0", + "name": "TESTGRPP0", "operation": "set", "requestId": "GroupRequest", - "commands": [ - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 8, - "image": "ADDGROUP TESTGRP0 ", - "messages": [ - "IKJ56702I INVALID GROUP, TESTGRP0" - ] - }, - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "ALTGROUP TESTGRP0 OMVS (GID (6667))" - } - ] + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "errorMessage": "Invalid attribute value specified.", + "errorOffset": 149, + "textInError": "name" + } }, - "returnCode": 4, - "reasonCode": 0 + "returnCode": 2000, + "reasonCode": 68 } } \ No newline at end of file diff --git a/tests/group/group_result_samples/add_group_result_error.xml b/tests/group/group_result_samples/add_group_result_error.xml index abb4c9fc..0a0aad6e 100644 --- a/tests/group/group_result_samples/add_group_result_error.xml +++ b/tests/group/group_result_samples/add_group_result_error.xml @@ -1,20 +1,15 @@ - - - 8 - 16 - 8 - ADDGROUP TESTGRP0 - IKJ56702I INVALID GROUP, TESTGRP0 - - - 0 - 0 - 0 - ALTGROUP TESTGRP0 OMVS (GID (6667)) - + + + 10 + 2000 + 68 + Invalid attribute value specified. + 149 + name + - 4 - 0 + 2000 + 68 \ 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 new file mode 100644 index 00000000..3d696bf3 --- /dev/null +++ b/tests/group/group_result_samples/extract_group_result_base_only_error.json @@ -0,0 +1,22 @@ +{ + "securityResult": { + "group": { + "name": "TESTGRP0", + "operation": "listdata", + "requestId": "GroupRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 4, + "image": "LISTGRP TESTGRP0 ", + "messages": [ + "ICH51003I NAME NOT FOUND IN RACF DATA SET" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} \ No newline at end of file diff --git a/tests/group/group_result_samples/extract_group_result_base_only_error.xml b/tests/group/group_result_samples/extract_group_result_base_only_error.xml new file mode 100644 index 00000000..9cef8fac --- /dev/null +++ b/tests/group/group_result_samples/extract_group_result_base_only_error.xml @@ -0,0 +1,14 @@ + + + + + 8 + 16 + 4 + LISTGRP TESTGRP0 + ICH51003I NAME NOT FOUND IN RACF DATA SET + + + 4 + 0 + \ No newline at end of file diff --git a/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.json b/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.json new file mode 100644 index 00000000..1ef0740a --- /dev/null +++ b/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.json @@ -0,0 +1,19 @@ +{ + "securityResult": { + "group": { + "name": "TESTGRPP0", + "operation": "listdata", + "requestId": "GroupRequest", + "error": { + "errorFunction": 10, + "errorCode": 2000, + "errorReason": 68, + "errorMessage": "Invalid attribute value specified.", + "errorOffset": 149, + "textInError": "name" + } + }, + "returnCode": 2000, + "reasonCode": 68 + } +} \ No newline at end of file diff --git a/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.xml b/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.xml new file mode 100644 index 00000000..ba6b7c61 --- /dev/null +++ b/tests/group/group_result_samples/extract_group_result_invalid_attribute_error.xml @@ -0,0 +1,15 @@ + + + + + 10 + 2000 + 68 + Invalid attribute value specified. + 149 + name + + + 2000 + 68 + \ No newline at end of file diff --git a/tests/group/test_group_constants.py b/tests/group/test_group_constants.py index 9da4f7d6..c22849b1 100644 --- a/tests/group/test_group_constants.py +++ b/tests/group/test_group_constants.py @@ -48,6 +48,18 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_JSON = get_sample( "extract_group_result_base_only_success.json" ) +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( + "extract_group_result_base_only_error.json" +) +TEST_EXTRACT_GROUP_RESULT_INVALID_ATTRIBUTE_ERROR_XML = get_sample( + "extract_group_result_invalid_attribute_error.xml" +) +TEST_EXTRACT_GROUP_RESULT_INVALID_ATTRIBUTE_ERROR_DICTIONARY = get_sample( + "extract_group_result_invalid_attribute_error.json" +) # Delete Group TEST_DELETE_GROUP_RESULT_SUCCESS_XML = get_sample("delete_group_result_success.xml") @@ -92,8 +104,8 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Debug Logging # ============================================================================ -TEST_ADD_GROUP_SUCCESS_LOG = get_sample("add_group_success.log") -TEST_ADD_GROUP_ERROR_LOG = get_sample("add_group_error.log") +TEST_ALTER_GROUP_SUCCESS_LOG = get_sample("alter_group_success.log") +TEST_ALTER_GROUP_ERROR_LOG = get_sample("alter_group_error.log") TEST_EXTRACT_GROUP_BASE_OMVS_SUCCESS_LOG = get_sample( "extract_group_base_omvs_success.log" diff --git a/tests/group/test_group_debug_logging.py b/tests/group/test_group_debug_logging.py index fad5e8f6..d4d63265 100644 --- a/tests/group/test_group_debug_logging.py +++ b/tests/group/test_group_debug_logging.py @@ -24,38 +24,43 @@ class TestGroupDebugLogging(unittest.TestCase): ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") # ============================================================================ - # Add Group + # Alter Group # ============================================================================ - def test_add_group_request_debug_log_works_on_success( + def test_alter_group_request_debug_log_works_on_success( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_SUCCESS_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - self.group_admin.add( - "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + self.group_admin.alter( + "TESTGRP0", traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_TRAITS ) success_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual(success_log, TestGroupConstants.TEST_ADD_GROUP_SUCCESS_LOG) + self.assertEqual(success_log, TestGroupConstants.TEST_ALTER_GROUP_SUCCESS_LOG) - def test_add_group_request_debug_log_works_on_error( + def test_alter_group_request_debug_log_works_on_error( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = TestGroupConstants.TEST_ADD_GROUP_RESULT_ERROR_XML + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ALTER_GROUP_RESULT_ERROR_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): try: - self.group_admin.add( - "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + self.group_admin.alter( + "TESTGRP0", + traits=TestGroupConstants.TEST_ALTER_GROUP_REQUEST_ERROR_TRAITS, ) except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual(error_log, TestGroupConstants.TEST_ADD_GROUP_ERROR_LOG) + self.assertEqual(error_log, TestGroupConstants.TEST_ALTER_GROUP_ERROR_LOG) # ============================================================================ # Extract Group diff --git a/tests/group/test_group_result_parser.py b/tests/group/test_group_result_parser.py index 0b81ba5c..0dd2760b 100644 --- a/tests/group/test_group_result_parser.py +++ b/tests/group/test_group_result_parser.py @@ -6,7 +6,12 @@ import __init__ import tests.group.test_group_constants as TestGroupConstants -from pyracf import AlterOperationError, GroupAdmin, SecurityRequestError +from pyracf import ( + AddOperationError, + AlterOperationError, + GroupAdmin, + SecurityRequestError, +) from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -26,9 +31,10 @@ def test_group_admin_can_parse_add_group_success_xml( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_ERROR_XML, + TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML, + ] self.assertEqual( self.group_admin.add( "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS @@ -36,19 +42,43 @@ def test_group_admin_can_parse_add_group_success_xml( TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_DICTIONARY, ) - # Error in environment, TESTGRP0 already added/exists + def test_group_admin_throws_error_on_add_existing_group( + self, + call_racf_mock: Mock, + ): + group_name = "TESTGRP0" + admin_name = "GROUP" + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_BASE_ONLY_SUCCESS_XML, + TestGroupConstants.TEST_ADD_GROUP_RESULT_SUCCESS_XML, + ] + with self.assertRaises(AddOperationError) as exception: + self.group_admin.add( + group_name, traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + ) + self.assertEqual( + exception.exception.message, + "Security request made to IRRSMO00 failed." + + "\n\nTarget profile " + + f"'{group_name}' already exists as a {admin_name} profile.", + ) + + # Error in command, TESTGRPP0 is invalid GROUP def test_group_admin_can_parse_add_group_error_xml( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = TestGroupConstants.TEST_ADD_GROUP_RESULT_ERROR_XML + call_racf_mock.side_effect = [ + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_INVALID_ATTRIBUTE_ERROR_XML, + TestGroupConstants.TEST_ADD_GROUP_RESULT_ERROR_XML, + ] with self.assertRaises(SecurityRequestError) as exception: self.group_admin.add( - "TESTGRP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS + "TESTGRPP0", traits=TestGroupConstants.TEST_ADD_GROUP_REQUEST_TRAITS ) self.assertEqual( exception.exception.result, - TestGroupConstants.TEST_ADD_GROUP_RESULT_ERROR_DICTIONARY, + TestGroupConstants.TEST_EXTRACT_GROUP_RESULT_INVALID_ATTRIBUTE_ERROR_DICTIONARY, ) # ============================================================================ diff --git a/tests/resource/resource_log_samples/add_resource_error.log b/tests/resource/resource_log_samples/add_resource_error.log deleted file mode 100644 index 4fcf03c2..00000000 --- a/tests/resource/resource_log_samples/add_resource_error.log +++ /dev/null @@ -1,107 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - ResourceAdmin.add() - - -{ - "base": { - "base:universal_access": { - "value": "None", - "operation": null - }, - "base:owner": { - "value": "eswift", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - ResourceAdmin.add() - - - - - - None - eswift - - - - - - [pyRACF:Debug] - Result XML - ResourceAdmin.add() - - - - - - - 8 - 16 - 8 - RDEFINE ELIXTEST (TESTING) - IKJ56702I INVALID CLASS, ELIXTEST - IKJ56701I MISSING ENTITY NAME+ - IKJ56701I MISSING NAME OF ENTITY IN SPECIFIED CLASS - - - 8 - 16 - 8 - RALTER ELIXTEST (TESTING) UACC (None) OWNER (eswift) - IKJ56702I INVALID CLASS, ELIXTEST - IKJ56712I INVALID KEYWORD, ) - - - 4 - 0 - - - - [pyRACF:Debug] - Result Dictionary - ResourceAdmin.add() - - -{ - "securityResult": { - "resource": { - "name": "TESTING", - "class": "ELIXTEST", - "operation": "set", - "requestId": "ResourceRequest", - "commands": [ - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 8, - "image": "RDEFINE ELIXTEST (TESTING)", - "messages": [ - "IKJ56702I INVALID CLASS, ELIXTEST", - "IKJ56701I MISSING ENTITY NAME+", - "IKJ56701I MISSING NAME OF ENTITY IN SPECIFIED CLASS" - ] - }, - { - "safReturnCode": 8, - "returnCode": 16, - "reasonCode": 8, - "image": "RALTER ELIXTEST (TESTING) UACC (None) OWNER (eswift)", - "messages": [ - "IKJ56702I INVALID CLASS, ELIXTEST", - "IKJ56712I INVALID KEYWORD, )" - ] - } - ] - }, - "returnCode": 4, - "reasonCode": 0 - } -} - diff --git a/tests/resource/resource_log_samples/add_resource_success.log b/tests/resource/resource_log_samples/add_resource_success.log deleted file mode 100644 index 8e8e6856..00000000 --- a/tests/resource/resource_log_samples/add_resource_success.log +++ /dev/null @@ -1,101 +0,0 @@ - - [pyRACF:Debug] - Request Dictionary - ResourceAdmin.add() - - -{ - "base": { - "base:universal_access": { - "value": "None", - "operation": null - }, - "base:owner": { - "value": "eswift", - "operation": null - } - } -} - - - [pyRACF:Debug] - Request XML - ResourceAdmin.add() - - - - - - None - eswift - - - - - - [pyRACF:Debug] - Result XML - ResourceAdmin.add() - - - - - - - 0 - 0 - 0 - RDEFINE ELIJTEST (TESTING) - ICH10006I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE ADDITION(S) UNTIL A SETROPTS REFRESH IS ISSUED. - - - 0 - 0 - 0 - RALTER ELIJTEST (TESTING) UACC (None) OWNER (eswift) - ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED. - - - 0 - 0 - - - - [pyRACF:Debug] - Result Dictionary - ResourceAdmin.add() - - -{ - "securityResult": { - "resource": { - "name": "TESTING", - "class": "ELIJTEST", - "operation": "set", - "requestId": "ResourceRequest", - "commands": [ - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "RDEFINE ELIJTEST (TESTING) ", - "messages": [ - "ICH10006I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE ADDITION(S) UNTIL A SETROPTS REFRESH IS ISSUED." - ] - }, - { - "safReturnCode": 0, - "returnCode": 0, - "reasonCode": 0, - "image": "RALTER ELIJTEST (TESTING) UACC (None) OWNER (eswift)", - "messages": [ - "ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." - ] - } - ] - }, - "returnCode": 0, - "reasonCode": 0 - } -} - diff --git a/tests/resource/resource_log_samples/alter_resource_error.log b/tests/resource/resource_log_samples/alter_resource_error.log new file mode 100644 index 00000000..922c6157 --- /dev/null +++ b/tests/resource/resource_log_samples/alter_resource_error.log @@ -0,0 +1,253 @@ + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + + 0 + 0 + 0 + RLIST ELIJTEST (TESTING) + CLASS NAME + ----- ---- + ELIJTEST TESTING + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ READ NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "messages": [ + "CLASS NAME", + "----- ----", + "ELIJTEST TESTING", + " ", + "LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING", + "----- -------- ---------------- ----------- -------", + " 00 ESWIFT READ READ NO", + " ", + "INSTALLATION DATA", + "-----------------", + "NONE", + " ", + "APPLICATION DATA", + "----------------", + "NONE", + " ", + "AUDITING", + "--------", + "FAILURES(READ)", + " ", + "NOTIFY", + "------", + "NO USER TO BE NOTIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "profiles": [ + { + "base": { + "class": "elijtest", + "name": "testing", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "yourAccess": "read", + "warning": null, + "installationData": null, + "applicationData": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "ALL", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + ALL + eswift + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 8 + 16 + 8 + RALTER ELIJTEST (TESTING) UACC (ALL) OWNER (eswift) + IKJ56702I INVALID UNIVERSAL ACCESS, ALL + IKJ56701I MISSING UNIVERSAL ACCESS+ + IKJ56701I MISSING ALTER, CONTROL, UPDATE, READ, EXECUTE, OR NONE + + + 4 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "RALTER ELIJTEST (TESTING) UACC (ALL) OWNER (eswift)", + "messages": [ + "IKJ56702I INVALID UNIVERSAL ACCESS, ALL", + "IKJ56701I MISSING UNIVERSAL ACCESS+", + "IKJ56701I MISSING ALTER, CONTROL, UPDATE, READ, EXECUTE, OR NONE" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} + diff --git a/tests/resource/resource_log_samples/alter_resource_success.log b/tests/resource/resource_log_samples/alter_resource_success.log new file mode 100644 index 00000000..5f5a6f9b --- /dev/null +++ b/tests/resource/resource_log_samples/alter_resource_success.log @@ -0,0 +1,249 @@ + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + + 0 + 0 + 0 + RLIST ELIJTEST (TESTING) + CLASS NAME + ----- ---- + ELIJTEST TESTING + + LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING + ----- -------- ---------------- ----------- ------- + 00 ESWIFT READ READ NO + + INSTALLATION DATA + ----------------- + NONE + + APPLICATION DATA + ---------------- + NONE + + AUDITING + -------- + FAILURES(READ) + + NOTIFY + ------ + NO USER TO BE NOTIFIED + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "messages": [ + "CLASS NAME", + "----- ----", + "ELIJTEST TESTING", + " ", + "LEVEL OWNER UNIVERSAL ACCESS YOUR ACCESS WARNING", + "----- -------- ---------------- ----------- -------", + " 00 ESWIFT READ READ NO", + " ", + "INSTALLATION DATA", + "-----------------", + "NONE", + " ", + "APPLICATION DATA", + "----------------", + "NONE", + " ", + "AUDITING", + "--------", + "FAILURES(READ)", + " ", + "NOTIFY", + "------", + "NO USER TO BE NOTIFIED" + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Result Dictionary (Formatted Profile) + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RLIST ELIJTEST (TESTING) ", + "profiles": [ + { + "base": { + "class": "elijtest", + "name": "testing", + "level": 0, + "owner": "eswift", + "universalAccess": "read", + "yourAccess": "read", + "warning": null, + "installationData": null, + "applicationData": null, + "auditing": { + "failures": "read" + }, + "notify": null, + "generic": false + } + } + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + + + [pyRACF:Debug] + Request Dictionary + ResourceAdmin.alter() + + +{ + "base": { + "base:universal_access": { + "value": "Read", + "operation": null + }, + "base:owner": { + "value": "eswift", + "operation": null + } + } +} + + + [pyRACF:Debug] + Request XML + ResourceAdmin.alter() + + + + + + Read + eswift + + + + + + [pyRACF:Debug] + Result XML + ResourceAdmin.alter() + + + + + + Definition exists. Add command skipped due to precheck option + + 0 + 0 + 0 + RALTER ELIJTEST (TESTING) UACC (Read) OWNER (eswift) + ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED. + + + 0 + 0 + + + + [pyRACF:Debug] + Result Dictionary + ResourceAdmin.alter() + + +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIJTEST", + "operation": "set", + "requestId": "ResourceRequest", + "info": [ + "Definition exists. Add command skipped due to precheck option" + ], + "commands": [ + { + "safReturnCode": 0, + "returnCode": 0, + "reasonCode": 0, + "image": "RALTER ELIJTEST (TESTING) UACC (Read) OWNER (eswift)", + "messages": [ + "ICH11009I RACLISTED PROFILES FOR ELIJTEST WILL NOT REFLECT THE UPDATE(S) UNTIL A SETROPTS REFRESH IS ISSUED." + ] + } + ] + }, + "returnCode": 0, + "reasonCode": 0 + } +} + diff --git a/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.json b/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.json new file mode 100644 index 00000000..dde77d51 --- /dev/null +++ b/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.json @@ -0,0 +1,25 @@ +{ + "securityResult": { + "resource": { + "name": "TESTING", + "class": "ELIXTEST", + "operation": "listdata", + "requestId": "ResourceRequest", + "commands": [ + { + "safReturnCode": 8, + "returnCode": 16, + "reasonCode": 8, + "image": "RLIST ELIXTEST (TESTING) ", + "messages": [ + "IKJ56702I INVALID CLASS NAME, ELIXTEST", + "IKJ56701I MISSING ENTITY NAME+", + "IKJ56701I MISSING NAME OF ENTITY TO BE LISTED" + ] + } + ] + }, + "returnCode": 4, + "reasonCode": 0 + } +} \ No newline at end of file diff --git a/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.xml b/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.xml new file mode 100644 index 00000000..6851ec10 --- /dev/null +++ b/tests/resource/resource_result_samples/extract_resource_result_invalid_class_error.xml @@ -0,0 +1,16 @@ + + + + + 8 + 16 + 8 + RLIST ELIXTEST (TESTING) + IKJ56702I INVALID CLASS NAME, ELIXTEST + IKJ56701I MISSING ENTITY NAME+ + IKJ56701I MISSING NAME OF ENTITY TO BE LISTED + + + 4 + 0 + \ No newline at end of file diff --git a/tests/resource/test_resource_constants.py b/tests/resource/test_resource_constants.py index 15538f05..f308e6f4 100644 --- a/tests/resource/test_resource_constants.py +++ b/tests/resource/test_resource_constants.py @@ -52,6 +52,12 @@ def get_sample(sample_file: str) -> Union[str, bytes]: TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_DICTIONARY = get_sample( "extract_resource_result_base_error.json" ) +TEST_EXTRACT_RESOURCE_RESULT_INVALID_CLASS_ERROR_XML = get_sample( + "extract_resource_result_invalid_class_error.xml" +) +TEST_EXTRACT_RESOURCE_RESULT_INVALID_CLASS_ERROR_DICTIONARY = get_sample( + "extract_resource_result_invalid_class_error.json" +) # Delete Resource TEST_DELETE_RESOURCE_RESULT_SUCCESS_XML = get_sample( @@ -107,8 +113,8 @@ def get_sample(sample_file: str) -> Union[str, bytes]: # Debug Logging # ============================================================================ -TEST_ADD_RESOURCE_SUCCESS_LOG = get_sample("add_resource_success.log") -TEST_ADD_RESOURCE_ERROR_LOG = get_sample("add_resource_error.log") +TEST_ALTER_RESOURCE_SUCCESS_LOG = get_sample("alter_resource_success.log") +TEST_ALTER_RESOURCE_ERROR_LOG = get_sample("alter_resource_error.log") TEST_EXTRACT_RESOURCE_BASE_SUCCESS_LOG = get_sample("extract_resource_base_success.log") TEST_EXTRACT_RESOURCE_BASE_ERROR_LOG = get_sample("extract_resource_base_error.log") diff --git a/tests/resource/test_resource_debug_logging.py b/tests/resource/test_resource_debug_logging.py index 2fac5c60..8342649d 100644 --- a/tests/resource/test_resource_debug_logging.py +++ b/tests/resource/test_resource_debug_logging.py @@ -24,46 +24,48 @@ class TestResourceDebugLogging(unittest.TestCase): ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") # ============================================================================ - # Add Resource + # Alter Resource # ============================================================================ - def test_add_resource_request_debug_log_works_on_success( + def test_alter_resource_request_debug_log_works_on_success( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_SUCCESS_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - self.resource_admin.add( + self.resource_admin.alter( "TESTING", "ELIJTEST", - traits=TestResourceConstants.TEST_ADD_RESOURCE_REQUEST_TRAITS, + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_TRAITS, ) success_log = self.ansi_escape.sub("", stdout.getvalue()) self.assertEqual( - success_log, TestResourceConstants.TEST_ADD_RESOURCE_SUCCESS_LOG + success_log, TestResourceConstants.TEST_ALTER_RESOURCE_SUCCESS_LOG ) - def test_add_resource_request_debug_log_works_on_error( + def test_alter_resource_request_debug_log_works_on_error( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_XML - ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ALTER_RESOURCE_RESULT_ERROR_XML, + ] stdout = io.StringIO() with contextlib.redirect_stdout(stdout): try: - self.resource_admin.add( + self.resource_admin.alter( "TESTING", - "ELIXTEST", - traits=TestResourceConstants.TEST_ADD_RESOURCE_REQUEST_ERROR_TRAITS, + "ELIJTEST", + traits=TestResourceConstants.TEST_ALTER_RESOURCE_REQUEST_ERROR_TRAITS, ) except SecurityRequestError: pass error_log = self.ansi_escape.sub("", stdout.getvalue()) - self.assertEqual(error_log, TestResourceConstants.TEST_ADD_RESOURCE_ERROR_LOG) + self.assertEqual(error_log, TestResourceConstants.TEST_ALTER_RESOURCE_ERROR_LOG) # ============================================================================ # Extract Resource diff --git a/tests/resource/test_resource_result_parser.py b/tests/resource/test_resource_result_parser.py index 7b5bc9b0..624157de 100644 --- a/tests/resource/test_resource_result_parser.py +++ b/tests/resource/test_resource_result_parser.py @@ -6,7 +6,12 @@ import __init__ import tests.resource.test_resource_constants as TestResourceConstants -from pyracf import AlterOperationError, ResourceAdmin, SecurityRequestError +from pyracf import ( + AddOperationError, + AlterOperationError, + ResourceAdmin, + SecurityRequestError, +) from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -26,27 +31,52 @@ def test_resource_admin_can_parse_add_resource_success_xml( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_XML - ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_ERROR_XML, + TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_XML, + ] self.assertEqual( self.resource_admin.add("TESTING", "ELIJTEST"), TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_DICTIONARY, ) + def test_resource_admin_throws_error_on_add_existing_profile( + self, + call_racf_mock: Mock, + ): + profile_name = "TESTING" + class_name = "ELIJTEST" + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_BASE_SUCCESS_XML, + TestResourceConstants.TEST_ADD_RESOURCE_RESULT_SUCCESS_XML, + ] + with self.assertRaises(AddOperationError) as exception: + self.resource_admin.add( + profile_name, + class_name, + traits=TestResourceConstants.TEST_ADD_RESOURCE_REQUEST_TRAITS, + ) + self.assertEqual( + exception.exception.message, + "Security request made to IRRSMO00 failed." + + "\n\nTarget profile " + + f"'{profile_name}' already exists as a profile in the {class_name} class.", + ) + # Error: Invalid Entity Name ELIXTEST def test_resource_admin_can_parse_add_resource_error_xml( self, call_racf_mock: Mock, ): - call_racf_mock.return_value = ( - TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_XML - ) + call_racf_mock.side_effect = [ + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_INVALID_CLASS_ERROR_XML, + TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_XML, + ] with self.assertRaises(SecurityRequestError) as exception: self.resource_admin.add("TESTING", "ELIXTEST") self.assertEqual( exception.exception.result, - TestResourceConstants.TEST_ADD_RESOURCE_RESULT_ERROR_DICTIONARY, + TestResourceConstants.TEST_EXTRACT_RESOURCE_RESULT_INVALID_CLASS_ERROR_DICTIONARY, ) # ============================================================================ diff --git a/tests/user/test_user_result_parser.py b/tests/user/test_user_result_parser.py index b19f165e..8096c33e 100644 --- a/tests/user/test_user_result_parser.py +++ b/tests/user/test_user_result_parser.py @@ -6,7 +6,12 @@ import __init__ import tests.user.test_user_constants as TestUserConstants -from pyracf import AlterOperationError, SecurityRequestError, UserAdmin +from pyracf import ( + AddOperationError, + AlterOperationError, + SecurityRequestError, + UserAdmin, +) from pyracf.common.irrsmo00 import IRRSMO00 # Resolves F401 @@ -41,6 +46,27 @@ def test_user_admin_can_parse_add_user_success_xml( TestUserConstants.TEST_ADD_USER_RESULT_SUCCESS_DICTIONARY, ) + def test_user_admin_throws_error_on_add_existing_user( + self, + call_racf_mock: Mock, + ): + profile_name = "squidwrd" + admin_name = "USER" + call_racf_mock.side_effect = [ + TestUserConstants.TEST_EXTRACT_USER_RESULT_BASE_ONLY_SUCCESS_XML, + TestUserConstants.TEST_ADD_USER_RESULT_SUCCESS_XML, + ] + with self.assertRaises(AddOperationError) as exception: + self.user_admin.add( + profile_name, traits=TestUserConstants.TEST_ADD_USER_REQUEST_TRAITS + ) + self.assertEqual( + exception.exception.message, + "Security request made to IRRSMO00 failed." + + "\n\nTarget profile " + + f"'{profile_name}' already exists as a {admin_name} profile.", + ) + # Error in command, SQUIDWARD is invalid USERID def test_user_admin_can_parse_add_user_error_xml( self,