diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..b1fe094 --- /dev/null +++ b/agents.md @@ -0,0 +1,204 @@ +# Agents Guide — RouteKit Classic ASP / Keycloak Test + +## What this project is + +A Classic ASP MVC web application running on IIS (Windows). It uses the **RouteKit** framework (front-controller pattern) with Keycloak OpenID Connect for authentication. The database is Microsoft Access (`.accdb`). There is no Node, npm, or build step — everything is server-side VBScript served by IIS. + +--- + +## Project layout + +``` +public/ IIS site root — point IIS here + Default.asp Front controller and route table + web.config App settings, URL rewrite rules + +core/ Framework internals — do not modify + autoload_core.asp + mvc.asp + router.wsc + lib.*.asp Core libraries (see below) + +app/ + controllers/ Controller classes (one per feature area) + views/ View partials (folders match controller name) + shared/ Header, footer, layout partials + models/ POBO (plain-old business objects) + repositories/ Data access classes + +db/ + migrations/ Sequential migration scripts + webdata.accdb Access database + +tests/ Dev-only aspunit test harness (separate IIS app) +scripts/ VBScript code generators +``` + +--- + +## Core libraries (core/lib.*.asp) + +| File | Purpose | +|---|---| +| `lib.Keycloak.asp` | OpenID Connect / Keycloak auth helper | +| `lib.Routes.asp` | Route registration and URL helpers | +| `lib.ControllerRegistry.asp` | Controller whitelist (security) | +| `lib.DAL.asp` | Database access layer | +| `lib.Data.asp` | Data helpers | +| `lib.Collections.asp` | Dictionary/list helpers | +| `lib.Enumerable.asp` | Collection iteration helpers | +| `lib.Validations.asp` | Input validation | +| `lib.Flash.asp` | Flash message helpers | +| `lib.FormCache.asp` | Form value re-population | +| `lib.HTML.asp` | HTML rendering helpers | +| `lib.HTML.Security.asp` | XSS escaping (`H()` function) | +| `lib.Strings.asp` | String utilities | +| `lib.Automapper.asp` | Object mapping helpers | +| `lib.ErrorHandler.asp` | Error handling | +| `lib.Migrations.asp` | Migration runner | +| `lib.json.asp` | JSON parsing/serialization | +| `lib.Upload.asp` | File upload helper | +| `lib.CDOEmail.asp` | Email via CDO | +| `lib.ad.auth.asp` | Active Directory auth | +| `lib.crypto.helper.asp` | Crypto utilities | + +--- + +## Routes + +Defined in [public/Default.asp](public/Default.asp): + +| Method | Path | Controller | Action | +|---|---|---|---| +| GET | `/` | HomeController | Index | +| GET | `/home` | HomeController | Index | +| GET | `/auth/login` | AuthController | Login | +| GET | `/auth/callback` | AuthController | Callback | +| GET | `/auth/logout` | AuthController | Logout | +| GET | `/404` | ErrorController | NotFound | + +All requests are rewritten through `Default.asp` by the IIS URL Rewrite rule. Static assets (`css/`, `js/`, `images/`, `favicon.ico`) bypass the rewrite. + +--- + +## Configuration (public/web.config appSettings) + +| Key | Description | +|---|---| +| `ConnectionString` | ACE OLEDB path to `webdata.accdb` | +| `Environment` | `Development`, `Staging`, or `Production` | +| `KeycloakBaseUrl` | Keycloak server base URL (no `/realms/...`) | +| `KeycloakRealm` | Keycloak realm name | +| `KeycloakClientId` | Client ID | +| `KeycloakClientSecret` | Client secret — never commit real values | +| `KeycloakRedirectUri` | Absolute callback URL, e.g. `http://localhost:8080/auth/callback` | +| `KeycloakLogoutRedirectUri` | Post-logout redirect URL | +| `KeycloakScope` | OIDC scopes (default: `openid profile email`) | +| `KeycloakEnableLogging` | `true`/`false` — diagnostic log for auth failures | +| `KeycloakLogPath` | Path to Keycloak log file | +| `EnableErrorLogging` | `true`/`false` | +| `ErrorLogPath` | Path to error log file | + +--- + +## Authentication (Keycloak) + +The helper in `core/lib.Keycloak.asp` implements the OpenID Connect authorization-code flow. + +**Key functions:** + +```vbscript +KeycloakLogin() ' Redirect to Keycloak +KeycloakHandleCallback() ' Exchange code, store tokens — returns True on success +KeycloakIsLoggedIn() ' True if access token is in Session +KeycloakCurrentUser() ' Returns userinfo dictionary +KeycloakAccessToken() ' Raw access token string +KeycloakRefreshToken() +KeycloakIdToken() +KeycloakTokenClaims(token) ' Decode JWT payload into dictionary +KeycloakRequireLogin(returnToPath) ' Gate a page — redirects if not logged in +KeycloakConsumePostLoginRedirectPath("/") ' Get and clear stored return path +KeycloakHasRealmRole("admin") ' Role check against ID token +KeycloakHasClientRole(clientId, role) +KeycloakLogout("") ' Clear session and redirect to Keycloak logout +``` + +Session keys use the `Keycloak_` prefix. Tokens are stored in `Session`, not cookies. + +--- + +## Adding a new feature — step by step + +### 1. Migration +```bat +cscript //nologo scripts\generateMigration.vbs create_my_table +cscript //nologo scripts\runMigrations.vbs +``` + +### 2. Model and repository +```bat +cscript //nologo scripts\GenerateRepo.vbs /table:my_table /pk:id +``` +Move generated files to `app/models/` and `app/repositories/`. + +### 3. Controller +```bat +cscript //nologo scripts\generateController.vbs MyController "Index;Show(id);Create;Store" +``` +Move generated file to `app/controllers/`. + +### 4. Wire it up +1. Register in [core/lib.ControllerRegistry.asp](core/lib.ControllerRegistry.asp): `RegisterController "mycontroller"` +2. Include in [app/controllers/autoload_controllers.asp](app/controllers/autoload_controllers.asp) +3. Add routes in [public/Default.asp](public/Default.asp) +4. Create views in `app/views/MyController/` + +--- + +## Testing + +Tests live in `tests/` and run as a **separate IIS application** (never through the production `public/` root). + +- Framework: `tests/aspunit/` (vendored) +- Runner: browse to `run-all.asp` in the test IIS app +- Manifest: `tests/test-manifest.asp` — register new test pages here manually +- Bootstrap: `tests/bootstrap.asp` — shared setup for all test pages + +Test tiers: + +| Folder | Use for | +|---|---| +| `tests/unit/` | Deterministic helper and registry tests | +| `tests/component/` | Controlled controller/object tests | +| `tests/integration/` | Router/dispatch smoke tests, config behavior, rendered-page capture | + +After changing `tests/web.config`, sync nested configs: +```bat +cscript //nologo tests\sync-webconfigs.vbs +``` + +Or sync and open in one step: +```bat +tests\run-tests.cmd +``` + +--- + +## Requirements + +- Windows Server / Windows with IIS +- Classic ASP enabled +- IIS URL Rewrite module +- Microsoft Access Database Engine (ACE OLEDB 12.0) +- Keycloak server (for auth flows) + +--- + +## Things to avoid + +- Do not modify files under `core/` — these are framework internals. +- Do not add controllers without registering them in `ControllerRegistry` — the MVC dispatcher will reject unregistered names. +- Do not commit real `KeycloakClientSecret` values — inject per environment. +- Do not add test routes or test pages under `public/`. +- Do not use the production `public/` IIS app to run tests. +- Always use `H()` from `lib.HTML.Security.asp` when rendering user-supplied data to prevent XSS.