# Permission List Table Access Report

> Maps a PeopleSoft Permission List to all SQL tables and views it grants Update and Read access to, tracing each record back to its granting components, pages, component interfaces, and query access trees.

---

Site [llms.txt](/llms.txt)

---

<div class="alert alert-primary border shadow-sm mb-4 psl-prospect-cta" role="note">
  <div class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3">
    <div>
      <strong>New to psLens?</strong> This page documents one specific report. To see how it runs in the product, what
      the output looks like, and how teams use it in practice, start with a live walkthrough.
    </div>
    <div class="d-flex flex-wrap gap-2">
      <a class="btn btn-sm btn-primary" href="/contact/">Book a Demo</a>
      <a class="btn btn-sm btn-outline-primary" href="/docs/reports/">Browse Reports</a>
    </div>
  </div>
</div><div id="pslens-context-panel" class="card border-info mb-4 d-none">
  <div class="card-header bg-light text-info py-2 fw-bold d-flex align-items-center border-bottom border-info-subtle">
    <i class="bi bi-info-circle-fill me-2"></i>
    <span>Tailored Operational Context</span>
  </div>
  <div class="card-body p-0">
    <ul class="list-group list-group-flush">
      <li id="row-db" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Target Database:</strong>
        <span id="ctx-db" class="badge bg-secondary font-monospace">&mdash;</span>
      </li>
      <li id="row-type" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Context Type:</strong>
        <span id="ctx-type" class="badge bg-light text-dark border font-monospace text-uppercase">&mdash;</span>
      </li>
      <li id="row-severity" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Alert Severity:</strong>
        <span id="ctx-severity" class="badge">&mdash;</span>
      </li>
      <li id="row-time" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Triggered Time:</strong>
        <span id="ctx-time" class="text-muted small">&mdash;</span>
      </li>
      <li id="row-details" class="list-group-item py-2 d-none">
        <strong id="label-details" class="d-block mb-1">Firing Context:</strong>
        <code id="ctx-details" class="d-block p-2 bg-light border rounded small"
          style="white-space: pre-wrap; word-break: break-all;">&mdash;</code>
      </li>
      <li id="row-sql" class="list-group-item py-2 d-none">
        <div class="d-flex justify-content-between align-items-center mb-1">
          <strong id="label-sql">Contextual SQL:</strong>
          <button id="btn-copy-sql" type="button" class="btn btn-sm btn-outline-secondary py-0 px-2 small">
            <i class="bi bi-clipboard me-1"></i>Copy SQL
          </button>
        </div>
        <pre class="mb-0 p-2 bg-light border rounded small"><code id="ctx-sql" style="white-space: pre-wrap; word-break: break-word;">&mdash;</code></pre>
      </li>
    </ul>
  </div>
</div>

<script>
  (function () {
    const params = new URLSearchParams(window.location.search);
    const metadata = params.get('metadata');
    if (!metadata) return;

    try {
      
      const base64 = metadata.replace(/-/g, '+').replace(/_/g, '/');
      const jsonStr = decodeURIComponent(escape(window.atob(base64)));
      const data = JSON.parse(jsonStr);

      if (data) {
        let hasData = false;

        if (data.db) {
          document.getElementById('ctx-db').textContent = data.db;
          document.getElementById('row-db').classList.remove('d-none');
          hasData = true;
        }

        if (data.type) {
          document.getElementById('ctx-type').textContent = data.type;
          document.getElementById('row-type').classList.remove('d-none');
          hasData = true;
        }

        if (data.severity) {
          const severityBadge = document.getElementById('ctx-severity');
          const severity = data.severity.toLowerCase();
          severityBadge.textContent = severity.toUpperCase();
          if (severity === 'critical') {
            severityBadge.className = 'badge bg-danger';
          } else if (severity === 'warning') {
            severityBadge.className = 'badge bg-warning text-dark';
          } else {
            severityBadge.className = 'badge bg-info';
          }
          document.getElementById('row-severity').classList.remove('d-none');
          hasData = true;
        }

        if (data.t) {
          const date = new Date(data.t * 1000);
          document.getElementById('ctx-time').textContent = date.toLocaleString();
          document.getElementById('row-time').classList.remove('d-none');
          hasData = true;
        }

        if (data.details) {
          document.getElementById('ctx-details').textContent = data.details;

          
          const labelDetails = document.getElementById('label-details');
          if (data.type === 'object') {
            labelDetails.textContent = 'Object Metadata Details:';
          } else if (data.type === 'report') {
            labelDetails.textContent = 'Report Description:';
          } else {
            labelDetails.textContent = 'Firing Context:';
          }

          document.getElementById('row-details').classList.remove('d-none');
          hasData = true;
        }

        if (data.sql) {
          document.getElementById('ctx-sql').textContent = data.sql;
          document.getElementById('row-sql').classList.remove('d-none');
          const copyBtn = document.getElementById('btn-copy-sql');
          if (copyBtn && navigator.clipboard) {
            copyBtn.addEventListener('click', function () {
              navigator.clipboard.writeText(data.sql).then(function () {
                copyBtn.innerHTML = '<i class="bi bi-check2 me-1"></i>Copied';
                setTimeout(function () {
                  copyBtn.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy SQL';
                }, 2000);
              });
            });
          }
          hasData = true;
        }

        if (hasData) {
          document.getElementById('pslens-context-panel').classList.remove('d-none');
        }
      }
    } catch (e) {
      console.error('Failed to parse operational context metadata:', e);
    }
  })();
</script>

## Permission List Table Access Report

**Report ID:** `security-permlist-table-access`
**Category:** Security
**Parameters:** `classid` (required) — the PeopleSoft Permission List (`CLASSID`) to audit

## Purpose

This report maps a single PeopleSoft Permission List (`CLASSID`) to every SQL Table and View (`PSRECDEFN`) it grants **Update** or **Read** access to, and traces each record back to the exact sources that grant that access:

- **Components & Pages** (`PSAUTHITEM` → `PSMENUITEM` → `PSPNLGRPDEFN` / `PSPNLGROUP` → `PSPNLFIELD` with nested subpages/secondary pages expanded and subrecords resolved)
- **Component Interfaces** (`PSAUTHBUSCOMP` → `PSBCITEM` collections and properties)
- **Query Access Trees** (`SCRTY_ACC_GRP` → `PSTREENODE` leaf records)

## Access Classification Rules

Only persistent SQL Tables (`RECTYPE IN (0, 7)`) and SQL Views (`RECTYPE IN (1, 5, 6)`) are included in the output. Derived/Work records (`RECTYPE = 2`) and unexpanded Subrecords (`RECTYPE = 3`) are excluded.

### Component Pages

- **Update + Read:** Granted when all of the following hold:
  - The menu authorization is not display-only (`PSAUTHITEM.DISPLAYONLY = 0`)
  - The component does not disable saving (`PSPNLGRPDEFN.DISABLESAVE = 0`)
  - The underlying record is a SQL Table or Temporary Table (`RECTYPE IN (0, 7)`)
  - The record has at least one editable page field (`FIELDUSE & 1 = 0`, excluding display-only, related display `FIELDUSE & 16 != 0`, static text, and visual lines) on that page or its nested subpages
- **Read-Only:** Granted when `DISPLAYONLY = 1`, `DISABLESAVE = 1`, the record is a SQL View (`RECTYPE IN (1, 5, 6)`), or all fields for that record on the page are display-only.

### Component Interfaces

- **Update + Read:** Granted when `PSAUTHBUSCOMP` authorizes `Save` or `Create` (`AUTHORIZEDACTIONS > 0`), the underlying record is a SQL Table (`RECTYPE IN (0, 7)`), and `PSBCITEM` exposes at least one Read/Write collection or property (`BCACCESS = 1`, `BCTYPE IN (3, 4)`).
- **Read-Only:** Granted when the Component Interface only has read methods (`Get`/`Find`), the record is a View, or all exposed items for the record are read-only (`BCACCESS = 2`). Search key items (`BCTYPE IN (1, 2, 5)`) are excluded.

### Query Access Trees

- **Read-Only:** All leaf records resolved from `PSTREENODE` via `SCRTY_ACC_GRP` are classified as Read-Only.

## Limitations & Scope

Because this report evaluates declarative PeopleTools metadata rather than executing runtime code, keep the following scope rules in mind:

- **PeopleCode & Batch SQL:** Direct database reads or writes executed inside PeopleCode (`SQLExec`, `Record.Insert`/`Update`/`Save`, Application Package classes) or batch processes (`PSAUTHPRCS` / Application Engine) are not traced by this report.
- **Component Search & Related-Display Records:** Component search records (`PSPNLGRPDEFN.SEARCHRECNAME` / `PSMENUITEM.SEARCHRECNAME`) and related-display fields (`FIELDUSE & 16 != 0`) are excluded so the report focuses on component buffer data tables rather than lookup/prompt tables.
- **Runtime Field Properties:** Page fields that are marked editable in `PSPNLFIELD` (including invisible page fields) are counted as editable even if component PeopleCode dynamically hides or disables them at runtime (`Field.DisplayOnly = True`).

## Tables Queried

|                     Table                     |                                              Purpose                                               |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `PSCLASSDEFN`                                 | Permission list definition and description                                                         |
| `PSAUTHITEM` + `PSMENUITEM`                   | Menu, component, and page item authorizations                                                      |
| `PSPNLGRPDEFN`                                | Component market and `DISABLESAVE` flag                                                            |
| `PSPNLGROUP`                                  | Component page item (`ITEMNAME`) to page definition (`PNLNAME`) mapping                            |
| `PSPNLFIELD`                                  | Page controls, subpage hierarchy, subrecord substitution, and field bitmasks (`FIELDUSE`)          |
| `PSRECDEFN`                                   | Record types (`RECTYPE`), physical SQL table names (`SQLTABLENAME`), and descriptions (`RECDESCR`) |
| `PSAUTHBUSCOMP` + `PSBCITEM`                  | Component interface method authorizations and collection/property bindings                         |
| `SCRTY_ACC_GRP` + `PSTREEDEFN` + `PSTREENODE` | Query access group trees and accessible leaf records                                               |

## How to Run

- **From a Permission List Detail Page:** Open any permission list and click **Run Table Access Report** in the right-hand **Reports** card.
- **From the Run Report Page:** Navigate to **Reports > Security Reports**, enter the Permission List name (`CLASSID`) in the **Permission List Table Access** card, and click **Run Now**.
