aboutsummaryrefslogtreecommitdiff
path: root/notebooks/racf_access_analysis.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'notebooks/racf_access_analysis.ipynb')
-rw-r--r--notebooks/racf_access_analysis.ipynb526
1 files changed, 526 insertions, 0 deletions
diff --git a/notebooks/racf_access_analysis.ipynb b/notebooks/racf_access_analysis.ipynb
new file mode 100644
index 0000000..48bea8d
--- /dev/null
+++ b/notebooks/racf_access_analysis.ipynb
@@ -0,0 +1,526 @@
+{
+ "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": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>Group</th>\n",
+ " <th>User</th>\n",
+ " <th>Access</th>\n",
+ " <th>Access Count</th>\n",
+ " <th>Universal Access</th>\n",
+ " <th>Attributes</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>0</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>DAF0</td>\n",
+ " <td>CREATE</td>\n",
+ " <td>0</td>\n",
+ " <td>READ</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IA0</td>\n",
+ " <td>CREATE</td>\n",
+ " <td>0</td>\n",
+ " <td>READ</td>\n",
+ " <td>ADSP SPECIAL OPERATIONS</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>AEH0</td>\n",
+ " <td>CREATE</td>\n",
+ " <td>0</td>\n",
+ " <td>READ</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>RESEARCH</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>0</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "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": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>Group</th>\n",
+ " <th>User</th>\n",
+ " <th>Access</th>\n",
+ " <th>Access Count</th>\n",
+ " <th>Universal Access</th>\n",
+ " <th>Attributes</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>0</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IA0</td>\n",
+ " <td>CREATE</td>\n",
+ " <td>0</td>\n",
+ " <td>READ</td>\n",
+ " <td>ADSP SPECIAL OPERATIONS</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>4</th>\n",
+ " <td>RESEARCH</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>0</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>6</th>\n",
+ " <td>RESEARCH</td>\n",
+ " <td>IA0</td>\n",
+ " <td>CONNECT</td>\n",
+ " <td>4</td>\n",
+ " <td>READ</td>\n",
+ " <td>ADSP SPECIAL OPERATIONS</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "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": [
+ "<div>\n",
+ "<style scoped>\n",
+ " .dataframe tbody tr th:only-of-type {\n",
+ " vertical-align: middle;\n",
+ " }\n",
+ "\n",
+ " .dataframe tbody tr th {\n",
+ " vertical-align: top;\n",
+ " }\n",
+ "\n",
+ " .dataframe thead th {\n",
+ " text-align: right;\n",
+ " }\n",
+ "</style>\n",
+ "<table border=\"1\" class=\"dataframe\">\n",
+ " <thead>\n",
+ " <tr style=\"text-align: right;\">\n",
+ " <th></th>\n",
+ " <th>Group</th>\n",
+ " <th>User</th>\n",
+ " <th>Access</th>\n",
+ " <th>Universal Access</th>\n",
+ " <th>Attributes</th>\n",
+ " <th>Notes</th>\n",
+ " </tr>\n",
+ " </thead>\n",
+ " <tbody>\n",
+ " <tr>\n",
+ " <th>0</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " <td>Review access appropriateness with system owner</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>1</th>\n",
+ " <td>PAYROLLB</td>\n",
+ " <td>IA0</td>\n",
+ " <td>CREATE</td>\n",
+ " <td>READ</td>\n",
+ " <td>ADSP SPECIAL OPERATIONS</td>\n",
+ " <td>Review access appropriateness with system owner</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>2</th>\n",
+ " <td>RESEARCH</td>\n",
+ " <td>IBMUSER</td>\n",
+ " <td>JOIN</td>\n",
+ " <td>ALTER</td>\n",
+ " <td>NONE</td>\n",
+ " <td>Review access appropriateness with system owner</td>\n",
+ " </tr>\n",
+ " <tr>\n",
+ " <th>3</th>\n",
+ " <td>RESEARCH</td>\n",
+ " <td>IA0</td>\n",
+ " <td>CONNECT</td>\n",
+ " <td>READ</td>\n",
+ " <td>ADSP SPECIAL OPERATIONS</td>\n",
+ " <td>Review access appropriateness with system owner</td>\n",
+ " </tr>\n",
+ " </tbody>\n",
+ "</table>\n",
+ "</div>"
+ ],
+ "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
+}