ambitus / pyracf

Python interface to the RACF Command interface.
https://ambitus.github.io/pyracf/
Apache License 2.0
10 stars 4 forks source link

Feature Request: SecurityRequestError Structure Standardization #44

Closed ElijahSwiftIBM closed 6 months ago

ElijahSwiftIBM commented 7 months ago

Is your feature request related to a problem? Please describe. When you run into "error" xml returned from IRRSMO00, there are effectively two kinds of structures:

Structure 1 is the case we based SecurityResultError on, RACF returns with a non-zero RC and the following XML:

<?xml version="1.0" encoding="IBM-1047"?>
<securityresult xmlns="http://www.ibm.com/systems/zos/saf/IRRSMO00Result1">
  <user name="TESTBOY" operation="set" requestid="UserRequest">
    <info>Definition exists. Add command skipped due  to precheck option</info>
    <command>
      <safreturncode>8</safreturncode>
      <returncode>16</returncode>
      <reasoncode>8</reasoncode>
      <image>ALTUSER TESTBOY  OMVS     (UID         (STRING))</image>
      <message>IKJ56701I MISSING OMVS UID+</message>
      <message>IKJ56701I MISSING OMVS USER ID (UID), 1-10 NUMERIC DIGITS</message>
      <message>IKJ56716I EXTRANEOUS INFORMATION WAS IGNORED: STRING</message>
    </command>
  </user>
  <returncode>4</returncode>
  <reasoncode>0</reasoncode>
</securityresult>

Structure 2 is the case where IRRSMO00 itself is returning an error to the user as XML:

<?xml version="1.0" encoding="IBM-1047"?>
<securityresult xmlns="http://www.ibm.com/systems/zos/saf/IRRSMO00Result1">
  <user name="TESTBOY" operation="set" requestid="UserRequest">
    <error>
      <errorfunction>10</errorfunction>
      <errorcode>2000</errorcode>
      <errorreason>76</errorreason>
      <errormessage>Data may not be specified for a boolean field.</errormessage>
      <erroroffset>216</erroroffset>
      <textinerror>STRING</textinerror>
    </error>
  </user>
  <returncode>2000</returncode>
  <reasoncode>76</reasoncode>
</securityresult>

Because the internal XML structure is so different, the SecurityRequestError is very different. Our documented sample of:

from pyracf import UserAdmin
from pyracf import SecurityRequestError

user_admin = UserAdmin()

try:
    user_admin.alter("squidwrd", traits={"base:password": "passwordtoolong"})
except SecurityRequestError as e:
    return_code = e.result["securityResult"]["user"]["returnCode"]
    reason_code = e.result["securityResult"]["user"]["reasonCode"]
    messages = "\n".join(e.result["securityResult"]["user"]["commands"][0]["messages"])
    print(f"Return Code: {return_code}")
    print(f"Reason Code: {reason_code}")
    print(f"Messages:\n\n{messages}")

would not work because there is no commands dictionary and no 'messages', just all sorts of error tags.

Describe the solution you'd like We should make one structure conform to the other at least when added under the SecurityRequestError object so that errors thrown either by SMO or RACF can be handled by users without having to change their exception handling logic.

Describe alternatives you've considered We could also split these into two entirely separate error objects to be handled differently, but that could still leave users with complications.

Additional context The SMO errors also have some nice fields like erroroffset and textinerror. It would be nice to incorporate these into the SecurityRequestError in some way so that end users could quickly access that information to correct user error.

lcarcaramo commented 6 months ago

Resolved in #57