Package: pyracf
pyracfPyRACF: A Python module for analyzing RACF securityPyRACF is a powerful Python module that simplifies the parsing and querying of any RACF database, providing an efficient and intuitive way to analyze security setups on IBM Z systems. By consuming the IRRDBU00 unload, PyRACF generates "Panda DataFrames" for each 'recordtype', which allow for seamless manipulation and analysis of the data.Pandas is a powerful data manipulation library in Python that allows for easy querying of the DataFrames generated by PyRACF. With Pandas, you can perform complex queries on the security data to extract meaningful insights into the security posture of your system.PyRACF's support for saving and loading pickle files makes it easier than ever to work with large RACF datasets, giving you the ability to perform comprehensive analyses of your security setup.For more information on the various records, please refer to theIBM documentationon IRRDBU00 record types and therecord formatsproduced by the database unload utility. The DataFrames generated by PyRACF feature the same 'fieldnames' as outlined in the documentation, ensuring consistency and accuracy in your analyses.To get started with PyRACF, install it usingpip install pyracfor explore the source code onGitHub. Use PyRACF to take control of your security data and protect your IBM Z systems from threats.Updates0.6.2 (Fix XLSX Creation)With newer versions of XlsxWriter there's no more .save(). Changed to .close()Pinned pandas and XlsxWriter versions in setup.py0.6.1 (Bug free?)XLS generation fully functional again (also for z/VM unloads)Oprhan detection working againConditional Dataset Access Records now parsing correctlyConditional Dataset Access now correctly pickled :)Fixed parsing of GRCACC records (had misparsed AUTH_ID)Conditional Generic (General) Records now with correct column name (GRCACC_CLASS_NAME)0.5.4 (Even more recordtypes!!)new property: genericConditionalAccess. Will show GRCACC records.Fixed some nasty 'default recordtypes' bugs0.5.0 (Pickle FTW!)new function: save_pickles(path=path, prefix=prefix). Will save all parsed dataframes as pickles (/path/prefixRECORDTYPE.pickle)Can now initialize RACF object from pickle-folder/prefix. To reuse earlier saves pickle files. See examples belowparse_fancycli now has two optional arguments (save_pickles and prefix) to also save pickle files after parsing to the directory as specified in save_pickles. The prefix argument is only useed with save_pickles isn't False0.4.5 (Fix Community Update Bug, thanks @Martydog)Group Connections now actually usable :)0.4.4Internal constants for all recordtypesImproved 'parse_fancycli()'0.4.3 (Community Update, thanks @Martydog)Add User Group Connections record 203Add User Installation Data record 2040.4.2Now XLS generation has more checks (fails gracefully if not all required records parsed, works when only genericAccess parsed)Same for Orphan detectionRecordtype 0503 (General Resource Members/genericMembers) addedParsing IRRDBU00 unloads like a boss>>> from pyracf import RACF
>>> mysys = RACF('/path/to/irrdbu00')
>>> mysys.parse()
>>> mysys.status
{'status': 'Still parsing your unload', 'lines-read': 200392, 'lines-parsed': 197269, 'lines-per-second': 63934, 'parse-time': 'n.a.'}
>>> mysys.status
{'status': 'Ready', 'lines-read': 7137540, 'lines-parsed': 2248149, 'lines-per-second': 145048, 'parse-time': 49.207921}Using Pickle Files>>> from pyracf import RACF
>>> mysys = RACF('/path/to/irrdbu00')
>>> mysys.parse_fancycli(save_pickles='/tmp/pickles', prefix='mysys-')
>>> hash(mysys.groups.values.tobytes())
-8566685915584060910Then later, you don't need to parse the same unload again, just do:>>> from pyracf import RACF
>>> mysys = RACF(pickles='/tmp/pickles', prefix='mysys-')
>>> hash(mysys.groups.values.tobytes())
-8566685915584060910All functionsFunction/PropertyExplanationExampleauditorsReturns DataFrame with all user having the auditor bit switched onmysys.auditorsconnectsReturns DataFrame with all user to group connectsmysys.connectsdatasetAccessReturns DataFrame with all Accesslists for all dataset profilesmysys.datasetsAccessdatasetsReturns DataFrame with all datasetprofilesmysys.datasetsgenericAccessReturns DataFrame with with all accesslists for generic resource profilesmysys.genericAccessgenericConditionalAccessReturns DataFrame with with all conditional accesslists for generic resource profilesmysys.genericConditionalAccessgenericsReturns DataFrame with with all generic resource profilesmysys.genericsgroupReturns DataFrame with with one dataset profile onlymysys.group('SYS1')groupConnectReturns DataFrame with with user group connection records (0203 recordtype)mysys.groupConnectgroupsReturns DataFrame with all group datamysys.groupsinstalldataReturns DataFrame with with user installation data (0204 recordtype)mysys.installdataoperationsReturns a DataFrame  with all operations usersmysys.operationsorphansReturns 2 DataFrames one with orphans in dataset profile access lists, and one for generic resourcesd, g = mysys.orphansparseparses the unload. optional specify recordtypesmysys.parse(recordtypes=['0200'])parse_fancycliparses the unload with a fancy cli status update. optional recordtypes can be specifiedmysys.parse_fancycli(recorddtypes=['0200'])revokedReturns a DataFrame  with all revoked usersmysys.revokedsave_picklesSaves all parsed types as pickle filesmysys.save_pickles(path='/tmp', prefix='mysys-')specialsReturns a DataFrame  with all special usersmysys.specialsstatusReturns JSON with parsing statusmysys.statusuacc_read_datasetsReturns a DataFrame  with all dataset profiles having UACC=READmysys.uacc_read_datasetsxlsCreates an XLSX with all permits per classmysys.xls(fileName='myxls.xlsx')Example use-caseGet all users that have not logged in (on?) since January 1st 2022. And print userID and last logon...import time
from pyracf import IRRDBU

mysys = IRRDBU('/path/to/irrdbu00')
mysys.parse()
while mysys.status['status'] != 'Ready':
    time.sleep(5)
selection = mysys.users.loc[mysys.users.USBD_LASTJOB_DATE<="2022-01-01"][['USBD_NAME','USBD_LASTJOB_DATE']]
for user in selection.values:
  print(f"Userid {user[0]}, last active: {user[1]}")Create a neat XLSXimport time
from pyracf import IRRDBU
mysys = IRRDBU('/path/to/irrdbu00')
mysys.parse()
while mysys.status['status'] != 'Ready':
    time.sleep(5)
mysys.xls('/path/to/my.xlsx')UpdatesIn this version we introduced IRRRDBU as an alternative to RACF. Examples have been updated. The RACF class from previous version is still available, but you're advised to change this to IRRDBU, as future version will have another user of the RACF class.Contribute to PyRACFIf you've some additions and/or bugfixes, feel free toforkthis repository, make your additions and fire a pull request.