Files
ipwhitelist-app/.github/AGENT-GUIDE.md
T

262 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IP Whitelist App — AI Agent Guide
> For AI coding agents (Copilot, etc.) to understand and continue development.
> Last updated: 2026-06-04 | Version: 0.5.23
## 1. Quick Overview
**Purpose:** Self-service portal for cloud provider customers to manage trusted IPv4 whitelist entries (CIDRs) that are excluded from DDoS filtering.
**Stack:** Node.js + Express + EJS + PostgreSQL
**Auth:** Mock RS256 JWT (dev) / Keycloak OIDC (production)
**Deployment:** PM2, behind nginx on a VM
## 2. Architecture
```
Browser → nginx:443 → Express:3001
├── /export (public, no auth)
├── /api/v1/* (Bearer JWT, REST API)
└── /* (UI layer, SSR EJS, session-based)
```
### Two-layer design:
- **API layer** (`src/api/routes/`): REST endpoints, Bearer JWT auth, direct DB access
- **UI layer** (`ui/routes/`): SSR EJS pages, session-based auth, proxies to API via HTTP (`ui/api-client.js`)
### Why two layers?
- API can be used directly by nginx/firewall for whitelist export
- UI provides human-friendly management interface
- Clear separation of concerns
### Key flow:
```
Browser → UI route → api-client.js (HTTP) → API route → PostgreSQL
```
## 3. Project Structure
```
ipwhitelist-app/
├── server.js # Entry point — mounts all routes
├── package.json
├── .env # Config (not in git)
├── .env.example # Config template
├── sql/schema.sql # Database schema
├── src/
│ ├── auth.js # Auth: mock JWT + OIDC/Keycloak
│ ├── config.js # MOCK_USERS + helpers
│ ├── db.js # PostgreSQL pool
│ ├── queries.js # All DB queries (CRUD, export, audit)
│ ├── validators.js # CIDR validation + blocked ranges + aggregation
│ │
│ ├── api/
│ │ ├── index.js # API router mount
│ │ └── routes/
│ │ ├── entries.js # GET/POST/PATCH/DELETE /entries + /entries/export
│ │ └── admin.js # GET /companies, PATCH /limit, GET /audit
│ │
│ ├── middleware/
│ │ ├── csrf.js # CSRF protection (NOT ACTIVE — tokens are empty)
│ │ ├── rateLimit.js # Rate limiters (auth, mutation, export)
│ │ └── session.js # express-session with pg store
│ │
│ └── routes/
│ ├── oidc.js # OIDC/Keycloak routes (inactive in mock mode)
│ └── (old monolithic routes deleted in v0.5.11)
├── ui/
│ ├── index.js # UI middleware: requireToken + resolveUser + mount routers
│ ├── api-client.js # HTTP client to /api/v1/* (from UI layer)
│ └── routes/
│ ├── auth.js # GET/POST /login, POST /login-token, GET /logout
│ ├── entries.js # GET /, POST /add, POST /edit/:id, POST /delete/:id
│ ├── admin.js # GET /admin, /audit (admin-only)
│ └── export.js # GET /export (proxies to API)
├── views/
│ ├── index.ejs # Main page: entries table + add/edit/delete forms
│ ├── ui-login.ejs # Login page (mock + manual clientId entry)
│ ├── admin.ejs # Admin: companies list + limits
│ ├── audit.ejs # Audit log
│ └── error.ejs # 404/500 error page
├── tests/
│ ├── api.js # 121 API integration tests (Bearer JWT)
│ ├── integration.js # 68 UI integration tests (session, EJS)
│ ├── tz-compliance.js # 47 ТЗ compliance tests
│ ├── tz-full-compliance.js # 111 comprehensive ТЗ tests (20 companies × 100 CIDRs)
│ ├── run-tests.js # 68 unit/smoke tests (no DB)
│ └── stress.js # 191 concurrency/stress tests
└── docs/
├── ТЗ.md # Tech spec (Russian)
├── ТЗ-плюс.md # Client clarifications
├── ТЗ-реализация.md # Implementation notes
├── keycloak-auth-reference.md # Keycloak/OIDC reference
├── devops-deploy.md # DevOps deployment guide
└── deploy-keycloak.md # Keycloak setup guide
```
## 4. Auth System
### Mock Mode (default, DEV_MODE=true)
- Local RSA keypair generated at startup
- `POST /login-token` with `clientId` field → mock JWT → session
- Mock users defined in `src/config.js`:
- `admin` (WZ01112) — admin@nubes.ru
- `alfa` (WZ88888) — alfa@example.com
- `multi` (WZ88888, WZ77777) — multi@example.com
- `beta` (WZ77777) — beta@example.com
### OIDC Mode (production, when KC_CLIENT_ID set)
- Authorization Code flow via Keycloak
- Routes in `src/routes/oidc.js` (inactive in mock mode)
- JWKS certificate verification
### UI resolveUser (ui/index.js)
- Decodes JWT from session (no signature check — API validates on each call)
- Extracts `ClientID`, `allClientIds`, `activeClientId`, `companyId`, `companyName`, `email`
- Sets `req.user` for all downstream handlers
### API bearerMiddleware (src/auth.js)
- Verifies Bearer JWT signature (RS256 via JWKS or mock keypair)
- Sets `req.user` via `userFromPayload()`
## 5. Multi-Company
Users can belong to multiple companies via IAM API profiles:
- `ClientID: "WZ88888, WZ77777"` → two companies
- Active company selector in UI (dropdown, `?switchTo=X` or JS navigation)
- API supports `?client_id=<id>` parameter to specify active company
- Default: first company in list (`allClientIds[0]`)
**IMPORTANT:** `req.user.activeClientId` must be synced BOTH in `session.user.activeClientId` AND `req.user.activeClientId` — see `ui/routes/entries.js` switch handler.
## 6. Company Isolation
- Each company is a row in `companies` table (unique by `client_id`)
- Entries are isolated by `company_id` foreign key
- Admin can view/edit any company via `?company=<db_id>`
- User sees only their company's entries
- Multi-company user switches via `?client_id=<clientId>`
## 7. Database
Three tables (see `sql/schema.sql`):
- `companies` — (id, client_id, name, custom_limit, created_at, updated_at)
- `whitelist_entries` — (id, company_id FK, value_cidr, comment, created_by, created_at, updated_at, deleted_at, deleted_by)
- `audit_log` — (id, user_email, company_id, action, old_value, new_value, entry_id, created_at)
Default limit: 15 entries per company (configurable via `DEFAULT_LIMIT` env).
## 8. Key Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/export` | None (public) | Aggregated CIDR list, text/plain |
| GET | `/api/v1/entries` | Bearer JWT | List entries for company |
| POST | `/api/v1/entries` | Bearer JWT | Create entry |
| PATCH | `/api/v1/entries/:id` | Bearer JWT | Update entry |
| DELETE | `/api/v1/entries/:id` | Bearer JWT | Soft-delete entry |
| GET | `/api/v1/entries/export` | Bearer JWT | Aggregated CIDR export |
| GET | `/api/v1/companies` | Bearer JWT (admin) | List companies |
| PATCH | `/api/v1/companies/:id/limit` | Bearer JWT (admin) | Set company limit |
| GET | `/api/v1/audit` | Bearer JWT (admin) | Audit log |
| GET | `/login` | None | Login page (UI) |
| POST | `/login` | None | Mock login (form) |
| POST | `/login-token` | None | Manual clientId login |
| GET | `/logout` | Session | Logout |
### Export query params:
- `?view=1` — inline (browser display)
- `?filename=X` — custom filename (default: white-list.txt)
## 9. CIDR Validation Rules
- Only IPv4, no IPv6
- Mask must be /22 to /32 (inclusive)
- Single IP without mask = /32
- 14 blocked ranges (RFC1918, CGNAT, loopback, etc.)
- No duplicates within a company
- No overlaps within a company
- Normalization: host bits zeroed (e.g., 9.9.9.5/24 → 9.9.9.0/24)
- Comment max 255 chars, optional
## 10. Running Tests
```bash
# Unit tests (no DB needed)
node tests/run-tests.js
# API tests (requires DB, DEV_MODE=true)
node tests/api.js
# UI integration tests
node tests/integration.js
# ТЗ compliance
node tests/tz-compliance.js
# Full ТЗ compliance (20 companies × 100 CIDRs)
node tests/tz-full-compliance.js
# Stress/concurrency tests
node tests/stress.js
```
All tests: 347+ PASS, 0 FAIL
## 11. Deployment
Deploy the app on a VM with nginx as reverse proxy. See `docs/devops-deploy.md` for details.
## 12. Common Pitfalls
### 🚨 Rate Limiter
`authLimiter`: 10 requests per 5 minutes. Hits quickly during testing. Reset after 5 min or use different IPs.
### 🚨 Mock Keypair Mismatch
Each `initAuth()` generates a NEW RSA keypair. When testing with supertest, the UI api-client makes HTTP requests to `localhost:3001` (PM2 instance), which has a DIFFERENT keypair → "invalid signature". Fix: test against the running PM2 instance directly, or stop PM2 and use the supertest port.
### 🚨 Company Switch Bug (FIXED v0.5.12)
`resolveUser` runs BEFORE the route handler. When switching companies via `?switchTo=X`, the session was updated but `req.user.activeClientId` still had the old value. Fixed by adding `req.user.activeClientId = switchTo` after session update.
### 🚨 CSRF Not Active
`csrfToken` is always `""` in templates. CSRF middleware is not mounted. Forms work, but there's no CSRF protection. To enable: mount `doubleCsrfProtection` middleware and call `generateCsrfToken()` in GET handlers.
### 🚨 `.env` on VM gets overwritten
Rsync excludes `.env` now, but double-check. If `.env` contains DB credentials, they MUST NOT be overwritten.
### 🚨 Version Must Always Increment
After ANY code change: bump `"version"` in `package.json`. PM2 shows version — user expects it to match.
## 13. OIDC/Keycloak Transition
To enable production auth:
1. DevOps creates client in Keycloak with redirect URI `/callback`
2. Set `.env`: `KC_CLIENT_ID`, `KC_CLIENT_SECRET`, `KC_BASE_URL`, `APP_URL`
3. Set `DEV_MODE=false`
4. `pm2 restart ipwhitelist --update-env`
5. Routes in `src/routes/oidc.js` activate automatically when `KC_CLIENT_ID` is set
See `docs/deploy-keycloak.md` for details.
## 14. Version History
| Version | Date | Key Changes |
|---------|------|-------------|
| 0.5.23 | 2026-06-04 | 0.0.0.0/8 blocked, security+edge test suites |
| 0.5.22 | 2026-06-04 | 4-audit cleanup, gitignore hardening |
| 0.5.21 | 2026-06-04 | Header injection fix, API timeout, nginx duplicate |
| 0.5.20 | 2026-06-04 | IAM fallback warning, fail-fast IAM_API_URL, X-Forwarded-Proto |
| 0.5.19 | 2026-06-04 | safeReturn in all redirects (open redirect fix) |
| 0.5.18 | 2026-06-04 | IAM API integration: fetchIamUser, switchProfile, profiles[] |
| 0.5.17 | 2026-06-04 | Previous stable |
| 0.5.14 | 2026-06-03 | Token login, JWKS in mock mode, production deploy docs |
| 0.5.13 | 2026-06-03 | OIDC routes prepared, .env.example, devops-deploy guide, repo cleanup |
| 0.5.12 | 2026-06-02 | Fixed company switch bug, aggregation, validation, TZ compliance |
| 0.5.11 | 2026-06-02 | Dead code cleanup, new integration tests, /export public |
| 0.5.10 | 2026-06-02 | UI/API split, mock users fixed, 121 API tests passing |