Chapter 9 — Programming II
Lesson map
Lesson 09 published. Map, example, EX025.
flowchart TD
N0["Learning outcomes"]
N1["9.1 Larger programs"]
N2["9.2 Parsing exports"]
N3["Lab — Role summary"]
N4["9.3 Reading the role summary"]
N5["9.4 Reproducibility"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Worked example"]
EX --> Q["Exercise EX025"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["9.1 Larger programs"]
N2["9.2 Parsing exports"]
N3["Lab — Role summary"]
N4["9.3 Reading the role summary"]
N5["9.4 Reproducibility"]
N6["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX025"]
[Learning outcomes]
|
v
[9.1 Larger programs]
|
v
[9.2 Parsing exports]
|
v
[Lab — Role summary]
|
v
[9.3 Reading the role summary]
|
v
[9.4 Reproducibility]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX025]
Learning outcomes
You will (1) build larger, structured programs; (2) parse identity exports; (3) handle errors and keep results reproducible.
9.1 Larger programs
As Sana's checks grow, she splits code into modules: readers for each export format, a common account model, comparison logic and report writers. Each module is small enough to understand. Functions have clear names and short docstrings, and there are no global variables that would make one part depend invisibly on another. This structure lets a colleague review the code and lets Sana add a new application's export by writing one reader, without touching the comparison logic that has already been tested.
9.2 Parsing exports
Application exports arrive in different formats: comma-separated files, directory listings and JSON from cloud services. A parser turns each into a common structure: username, display name, status, last sign-in and roles. Sana writes parsers against synthetic samples first, and each parser reports lines it cannot understand rather than silently skipping them. A silently skipped line could hide exactly the stale administrator account the check exists to find, so visible errors are a safety feature, not an inconvenience. Each parser has its own tests using small synthetic files.
Lab — Role summary
"""Summarise synthetic role assignments: members per role and risky permission combinations (stdlib only)."""
from collections import defaultdict
roles = {"clerk": {"view", "payments-raise"}, "clerk-plus": {"view", "payments-raise", "export"}, "approver": {"view", "payments-approve"},
"manager": {"view", "payments-approve", "reports"}, "manager-temp": {"view", "payments-approve", "reports", "export"},
"auditor": {"view", "reports"}, "helpdesk": {"view", "reset"}}
assign = {"u01": ["clerk"], "u02": ["clerk"], "u03": ["clerk"], "u04": ["clerk-plus"], "u05": ["approver"], "u06": ["approver"],
"u07": ["manager"], "u08": ["manager"], "u09": ["manager-temp"], "u10": ["auditor"], "u11": ["helpdesk"], "u12": ["clerk", "approver"]}
members = defaultdict(int)
for u, rs in assign.items():
for r in rs: members[r] += 1
print(f"accounts={len(assign)} roles={len(roles)}")
for r in roles: print(f" {r:13} members={members[r]}")
print("single-member roles:", sorted(r for r in roles if members[r] == 1))
for u, rs in sorted(assign.items()):
p = set().union(*(roles[r] for r in rs))
if {"payments-raise", "payments-approve"} <= p: print("raise+approve:", u, rs)
Run with python3 lab_roles.py. Verified output:
accounts=12 roles=7
clerk members=4
clerk-plus members=1
approver members=3
manager members=2
manager-temp members=1
auditor members=1
helpdesk members=1
single-member roles: ['auditor', 'clerk-plus', 'helpdesk', 'manager-temp']
raise+approve: u12 ['clerk', 'approver']
9.3 Reading the role summary
The synthetic export lists twelve accounts across seven roles. Four roles have only one member each, and two of those differ from a larger role by a single permission. This is the early stage of role explosion described in chapter 2. The script also shows one account holding both payments-raise and payments-approve through different roles. Sana records these as findings for the role design work later in the project, noting that the data is synthetic and the real export must be checked with each application owner.
9.4 Reproducibility
A result is reproducible when someone else can run the same program on the same input and get the same output. Sana records the script version, the export file and its date, and avoids hidden dependencies such as the current date unless it is passed in explicitly. Reports include these details in a header. When an auditor later asks how a finding was produced, Sana can show exactly which script and data created it, which turns analysis into evidence the council can rely on.
Worked example
The role summary finds seven roles for twelve accounts, four single-member roles and one account combining raise and approve permissions.
Synthetic case. Exports, roles and accounts are synthetic.
Errors and limits
Parsers that silently skip unreadable lines can hide the very accounts a check is meant to find.
Exercises
- EX025 — Single-member roles. How many single-member roles does the lab find? Rubric: 3 = four, two differing by one permission;; 2 = four;; 1 = vague;; 0 = none.
- EX026 — Combined permissions. What risky combination does the lab find? Rubric: 3 = one account with payments-raise and payments-approve;; 2 = raise and approve;; 1 = vague;; 0 = none.
- EX027 — Report header. What does Sana put in each report header? Rubric: 3 = script version, export file and date;; 2 = version;; 1 = name;; 0 = none.
Synthesis
Modular, error-reporting, reproducible programs make identity analysis dependable across many applications.
Continue studying — chapter 09 of 50
← Chapter 08 (draft) · Course chapter index · Chapter 10 (draft) →