Skip to main content

Windows Impersonation and Delegation

The intranet route of Module 2 (Windows sign-in through the host's Negotiate challenge, the grant mapped from the account SID) now opens on the confidential-documents reader: the blue AccessOps · Confidential documents window with the area strip, the list Document · Title · Folder · Last read (DOC-201 Salary bands 2027 labuser-only, DOC-202 Vendor contract archive pool-only, DOC-203 Audit sample (F03) labuser-only, DOC-204 Tenant B legal hold tenant-b), the Identity trace table (Document · Step · App subject · Request identity · Effective token · Outcome) and the status line. Clicking a document sends only its ID: DocumentCatalog.ResolveApprovedPath runs the Module 1 resource policy first and maps the ID to a canonical path under one approved root, then ScopedDocumentReader.Read reads the file inside WindowsIdentity.RunImpersonated with the request's Windows token and records the before / inside / after rows. Four plain messages: "DOC-201 opened · effective identity back to the pool", "DOC-202 could not be opened with your Windows account. The request was logged.", "DOC-203 could not be opened. The file is missing; the request was logged.", "DOC-204 is outside your access. Nothing was opened." — no path, SID or exception text on screen.

Solution: AccessOps.slnx = AccessOps/ (net10.0, Wisej-4 4.1.7, Microsoft.Data.Sqlite 10.0.14, Microsoft.AspNetCore.Authentication.Negotiate 10.0.14) + PolicyHarness/. Fixture script tools/Setup-Fixtures.ps1, IIS location file iis/ (from Module 2), written deliverables in docs/. Data and fixture files in AccessOpsData/ (git-ignored). Built from Module 2 (its IIS, challenge and provider docs stay in Module 2/docs).

Run it​

cd "Security and Auth Patterns Course\Module 3"
powershell -ExecutionPolicy Bypass -File tools\Setup-Fixtures.ps1 # approved root + ACLs (re-runnable; -Remove to clean up)
dotnet build AccessOps.slnx
dotnet run --project AccessOps --urls http://localhost:6403
dotnet run --project PolicyHarness

Open http://localhost:6403 in Edge/Chrome on the same machine (silent Negotiate for localhost; see Module 2). The Debug console prints one AccessOps.IdentityTrace line per row, with the SIDs, the token's logon group, the path and the exception — the detail the screen leaves out.

In the lab environment: tools\Setup-Fixtures.ps1 -PoolAccount "IIS APPPOOL\AccessOps Intranet" -LabUser "LAB\dana.ortiz", publish as in Module 2/docs/iis-publish-and-rollback.md, and set AccessOps:ApprovedRoot if the fixtures live elsewhere.

Demo accounts (invented, local only)​

As in Module 2: no passwords; the Windows account running the app is mapped to reader1 (Reader, tenant A) — --AccessOps:CurrentWindowsUser=visitor1 for an account without a grant (every document is then "outside your access", zero scopes opened — the same rule as the harness case "no Reader (M3)"; not clicked through in the app for this module).

What to try​

ActionExpected result
Open the pagethe list with four documents, "—" in Last read, empty Identity trace, status "Ready"
Click DOC-201rows Before · DEVBOX\you · Readable as pool, Inside · you · Impersonation · Read 18 KB, After · you · Scope closed (tinted); chip Read as user; status (green) "DOC-201 opened · effective identity back to the pool"
Click DOC-202Before · Access denied (ACL), Inside · … · Impersonation · Access denied (ACL), After · Reverted; chip Denied (ACL); status (red) "DOC-202 could not be opened with your Windows account. The request was logged."
Click DOC-203 (F03)rows labelled DOC-203 F03: Before · File missing, Inside · Impersonation · FileNotFoundException, After · Reverted; chip File missing; status (amber) "DOC-203 could not be opened. The file is missing; the request was logged."
Click DOC-204one row Policy · Denied before any scope; chip Not allowed; status (red) "DOC-204 is outside your access. Nothing was opened."; audit scoped-read B / DOC-204 denied tenant, no file.read
App logper row identity-trace … effective=… effectiveSid=… level=None/Impersonation logon=… outcome=… detail=<path or exception>; audit scoped-read (policy) + file.read (outcome) with one correlation ID
dotnet run --project PolicyHarnessModule 1/2 cases plus six Module 3 cases: allowed DOC-201 and approved F03 open a scope; tenant B, no Reader, unknown ID, a catalog row escaping the root: denied at zero scopes — docs/policy-harness-output.txt

Where this sample differs from the lesson or video​

  • Navigation: the video window has a left menu (Dashboard · Documents · Approvals · Employees); the sample keeps the course's area strip (Departments · Employees · Documents · Approvals · Admin console).
  • Identities in the trace: LAB\dana.ortiz vs IIS APPPOOL\AccessOps; here the pool and the user are the same account (DEVBOX\…), and only · Impersonation marks the inside row. Hence DOC-201 reads "Readable as pool" before the scope (video: "Access denied (ACL)"), and DOC-202 is denied before and inside (video intro chip "Read as pool"): local mode cannot flip the outcome.
  • Before row for F03: only Inside/After for DOC-203; the sample records Before too (the lab compares before and after).
  • Folder of DOC-204: the video column says "outside grant"; the real fixture folder is tenant-b.
  • App subject: acct …7f3a (redacted account ID, as in Modules 1–2) instead of the acct 4127.
  • Document IDs are strings (DOC-201); the lesson's code takes int documentId. The signature is otherwise the lesson's: Read(HttpContext context, string documentId).
  • How a read starts: no button; a click on a document row runs the read.
  • Request identity source: the lesson's code reads context.User; the sample passes the current request from IHttpContextAccessor (the WebSocket request), because Application.User on a WebSocket event is the page-load identity whose token may already be disposed (observed, Module 2 README).
  • Baseline read: the before row opens (does not read) the file as the process — a lab probe, so the matrix has a baseline outcome; production code would not touch the file outside the scope.
  • Document list: lists all four catalog IDs (the lab needs DOC-204 to show the policy deny); the read itself is always decided by the policy.
  • Logon group in the log: the trace log adds the token's logon-type group (INTERACTIVE/NETWORK/ SERVICE/BATCH), which is how the loopback-token fact was found.
  • IdentityTrace is the Module 3 class; Module 1–2's Debug lifecycle logger is renamed LifecycleTrace.