{ "cells": [ { "cell_type": "markdown", "id": "4c3a8609", "metadata": {}, "source": [ "\n", "# 🛡️ RACF Access Report Analysis\n", "\n", "This notebook demonstrates how to parse a RACF-like mainframe access report stored in a fixed-width text format, extract user access details, identify unusual access configurations, and summarize the results for follow-up.\n", "\n", "We'll be working with the file `sample_racf_data.txt`.\n", " " ] }, { "cell_type": "markdown", "id": "a0b3e2b2", "metadata": {}, "source": [ "\n", "## 📦 Install Dependencies\n", "\n", "If you haven't already installed `pandas`, run:\n", "\n", "```bash\n", "pip install pandas\n", "```\n", " " ] }, { "cell_type": "code", "execution_count": 1, "id": "ca527b49", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import re" ] }, { "cell_type": "markdown", "id": "d1694149", "metadata": {}, "source": [ "## 📂 Load RACF Report" ] }, { "cell_type": "code", "execution_count": 3, "id": "4891d98b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "LISTGRP *\n", "INFORMATION FOR GROUP PAYROLLB\n", "SUPERIOR GROUP=RESEARCH OWNER=IBMUSER CREATED=06.123\n", "NO INSTALLATION DATA\n", "NO MODEL DATA SET\n", "TERMUACC\n", "NO SUBGROUPS\n", "USER(S)= ACCESS= ACCESS COUNT= UNIVERSAL ACCESS=\n", "IBMUSER JOIN 000000 ALTER\n", "CONNECT ATTRIBUTES=NONE\n", "REVOKE DATE=NONE RESUME DATE=NONE\n", "DAF0 CREATE 000000 READ\n", "CONNECT ATTRIBUTES=NONE\n", "REVOKE DATE=NONE RESUME DATE=NONE\n", "IA0 CREATE 000000 READ\n", "CONNECT ATTRIBUTES=ADSP SPECIAL OPERATIONS\n", "REVOKE DATE=NONE RESUME DATE=NONE\n", "AEH0 CREATE 000000 READ\n", "CONNECT ATTRIBUTES=NONE\n", "REVOKE DATE=NONE RESUME DATE=NONE\n" ] } ], "source": [ "with open(\"sample_racf_data.txt\", \"r\") as file:\n", " lines = file.readlines()\n", "\n", "# Preview first 20 lines\n", "for line in lines[:20]:\n", " print(line.strip())" ] }, { "cell_type": "markdown", "id": "9d5ee64a", "metadata": {}, "source": [ "## 📝 Parse User Access Records" ] }, { "cell_type": "code", "execution_count": 4, "id": "18f06bc9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
GroupUserAccessAccess CountUniversal AccessAttributes
0PAYROLLBIBMUSERJOIN0ALTERNONE
1PAYROLLBDAF0CREATE0READNONE
2PAYROLLBIA0CREATE0READADSP SPECIAL OPERATIONS
3PAYROLLBAEH0CREATE0READNONE
4RESEARCHIBMUSERJOIN0ALTERNONE
\n", "
" ], "text/plain": [ " Group User Access Access Count Universal Access \\\n", "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", "1 PAYROLLB DAF0 CREATE 0 READ \n", "2 PAYROLLB IA0 CREATE 0 READ \n", "3 PAYROLLB AEH0 CREATE 0 READ \n", "4 RESEARCH IBMUSER JOIN 0 ALTER \n", "\n", " Attributes \n", "0 NONE \n", "1 NONE \n", "2 ADSP SPECIAL OPERATIONS \n", "3 NONE \n", "4 NONE " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Initialize lists to hold parsed records\n", "records = []\n", "current_group = \"\"\n", "\n", "for i, line in enumerate(lines):\n", " if \"INFORMATION FOR GROUP\" in line:\n", " current_group = line.strip().split()[-1]\n", "\n", " # Identify user lines: starts with a non-empty, non-space string followed by access keywords\n", " match = re.match(r\"^\\s*(\\S+)\\s+(JOIN|CREATE|CONNECT|USE)\\s+(\\d{6})\\s+(\\S+)\", line)\n", " if match:\n", " user, access, access_count, universal_access = match.groups()\n", "\n", " # Look ahead for CONNECT ATTRIBUTES line\n", " attr_line = lines[i + 1].strip() if (i + 1) < len(lines) else \"\"\n", " attr_match = re.search(r\"CONNECT ATTRIBUTES=(.*)\", attr_line)\n", " attributes = attr_match.group(1) if attr_match else \"NONE\"\n", "\n", " records.append(\n", " {\n", " \"Group\": current_group,\n", " \"User\": user,\n", " \"Access\": access,\n", " \"Access Count\": int(access_count),\n", " \"Universal Access\": universal_access,\n", " \"Attributes\": attributes,\n", " }\n", " )\n", "\n", "# Convert to DataFrame\n", "df = pd.DataFrame(records)\n", "df.head()" ] }, { "cell_type": "markdown", "id": "ab5546a6", "metadata": {}, "source": [ "## 📊 Analyze Access Data" ] }, { "cell_type": "code", "execution_count": 5, "id": "d1b8269a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Access\n", "CREATE 5\n", "JOIN 4\n", "USE 3\n", "CONNECT 1\n", "Name: count, dtype: int64\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
GroupUserAccessAccess CountUniversal AccessAttributes
0PAYROLLBIBMUSERJOIN0ALTERNONE
2PAYROLLBIA0CREATE0READADSP SPECIAL OPERATIONS
4RESEARCHIBMUSERJOIN0ALTERNONE
6RESEARCHIA0CONNECT4READADSP SPECIAL OPERATIONS
\n", "
" ], "text/plain": [ " Group User Access Access Count Universal Access \\\n", "0 PAYROLLB IBMUSER JOIN 0 ALTER \n", "2 PAYROLLB IA0 CREATE 0 READ \n", "4 RESEARCH IBMUSER JOIN 0 ALTER \n", "6 RESEARCH IA0 CONNECT 4 READ \n", "\n", " Attributes \n", "0 NONE \n", "2 ADSP SPECIAL OPERATIONS \n", "4 NONE \n", "6 ADSP SPECIAL OPERATIONS " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Count users by Access type\n", "access_summary = df[\"Access\"].value_counts()\n", "print(access_summary)\n", "\n", "# Identify users with ALTER access or SPECIAL OPERATIONS attribute\n", "anomalies = df[\n", " (df[\"Universal Access\"] == \"ALTER\")\n", " | (df[\"Attributes\"].str.contains(\"SPECIAL OPERATIONS\"))\n", "]\n", "\n", "anomalies" ] }, { "cell_type": "markdown", "id": "38201382", "metadata": {}, "source": [ "## 📑 Prepare Follow-Up Report" ] }, { "cell_type": "code", "execution_count": 6, "id": "a885710c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
GroupUserAccessUniversal AccessAttributesNotes
0PAYROLLBIBMUSERJOINALTERNONEReview access appropriateness with system owner
1PAYROLLBIA0CREATEREADADSP SPECIAL OPERATIONSReview access appropriateness with system owner
2RESEARCHIBMUSERJOINALTERNONEReview access appropriateness with system owner
3RESEARCHIA0CONNECTREADADSP SPECIAL OPERATIONSReview access appropriateness with system owner
\n", "
" ], "text/plain": [ " Group User Access Universal Access Attributes \\\n", "0 PAYROLLB IBMUSER JOIN ALTER NONE \n", "1 PAYROLLB IA0 CREATE READ ADSP SPECIAL OPERATIONS \n", "2 RESEARCH IBMUSER JOIN ALTER NONE \n", "3 RESEARCH IA0 CONNECT READ ADSP SPECIAL OPERATIONS \n", "\n", " Notes \n", "0 Review access appropriateness with system owner \n", "1 Review access appropriateness with system owner \n", "2 Review access appropriateness with system owner \n", "3 Review access appropriateness with system owner " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a concise follow-up report\n", "follow_up = anomalies[\n", " [\"Group\", \"User\", \"Access\", \"Universal Access\", \"Attributes\"]\n", "].copy()\n", "follow_up[\"Notes\"] = \"Review access appropriateness with system owner\"\n", "\n", "follow_up.reset_index(drop=True, inplace=True)\n", "follow_up" ] }, { "cell_type": "markdown", "id": "0158f787", "metadata": {}, "source": [ "\n", "## 🎉 Summary\n", "\n", "In this notebook, we:\n", "- Parsed a RACF-like access report from a fixed-width text file\n", "- Extracted key fields into a structured DataFrame\n", "- Analyzed access configurations for high-risk permissions\n", "- Summarized anomalies requiring follow-up with system owners\n", "\n", "🖥️ Use this as a starting point for mainframe audit automation projects!\n", " " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" } }, "nbformat": 4, "nbformat_minor": 5 }