## https://sploitus.com/exploit?id=B7E559EF-D75D-5EF9-8719-8055B9CE28E6
# RM OneView โ Proof of Concept
A working POC of the Relationship Manager daily dashboard, built to demonstrate
the **production security model** with dummy data: AD-style login โ server-side
identity โ RM-scoped data โ audit logging. Nothing is enforced in the browser.
---
## What this proves
1. **Username/password login** that mimics AD sign-in.
2. **Server-side entitlement** โ every data query is filtered by the logged-in
RM's ID *on the server*. An RM literally cannot request another RM's book.
3. **Role separation** โ RMs get the RM dashboard; managers get a team-aggregate
view (no individual client PII at the manager level).
4. **Audit logging** โ every login and data access is recorded.
5. **Rules-based recommendation engine** โ leads/opportunities are *derived* from
client data by transparent if-then rules (e.g. `balance>โน2Cr & products=1`),
ranked with bonus-gate impact on top.
6. **GenAI call-prep brief** โ on any lead, "โฆ Prep brief" generates an
AI-assisted briefing. Critically, it demonstrates the **de-identification
boundary** required for external-API GenAI (see below).
---
## GenAI: the de-identification boundary
This bank can only use **external** LLM APIs (no in-house model). Sending real
client PII to an external API is not acceptable, so the call-prep brief shows the
only defensible pattern:
1. **Entitlement check** โ the client must be in the requesting RM's book
(same server-side join as everything else; briefing another RM's client โ 404).
2. **De-identify** โ strip name/IDs; build a neutral, token-based payload
(`"CLIENT_A", UHNI, idle balance high, no login 71d`). Balances become bands,
not exact figures.
3. **External call** โ only that payload leaves the perimeter. *(In the POC this
is a mock `mock_llm()` with no network call; in production it's an HTTPS call
to the approved endpoint sending exactly this payload.)*
4. **Re-identify locally** โ the real name is re-inserted inside your environment,
never sent out.
The brief modal deliberately **shows the RM the exact de-identified payload that
left the perimeter**, so the privacy boundary is visible, not hidden. Every brief
is audit-logged as `BRIEF_GENERATED (de-identified payload sent)`.
> **Compliance caveat:** even de-identified, some banks prohibit *any*
> client-derived data leaving. Get sign-off before wiring a real external API.
> The core ranking/gate engine stays fully deterministic and on-prem โ GenAI is
> only at the language edge (briefs, drafting), never computing KRAs or advice.
---
## How it maps to the real architecture
| POC stand-in | Production equivalent |
|---|---|
| SQLite `rm_oneview.db` | On-prem BI **Postgres** (daily refresh) |
| `rm_client_map` table | The RM-to-client mapping already in your BI DB |
| Username/password form | **AD / ADFS** via SAML/OIDC (or LDAPS bind) |
| `employee_id` as the key | The shared employee-ID key common to AD + BI DB |
| Server-side `WHERE rm_id=?` filter | Same query **+ Postgres row-level security** |
| `role` column (`RM`/`MANAGER`) | **AD group** membership |
| In-memory session dict | Signed session cookie / server session store |
| `audit_log` table | Your enterprise audit/SIEM sink |
| FastAPI on localhost | App server hosted **on-prem**, behind corporate VPN |
The entitlement SQL is written exactly as it runs against Postgres, so porting is
mostly a database-driver swap plus wiring real AD auth in place of the login form.
---
## Run it locally
Requires Python 3.10+.
```bash
# 1. install dependencies
pip install fastapi uvicorn python-multipart
# 2. build the dummy database (10 RMs, 2 managers, ~200 clients)
python seed_data.py
# 3. start the server
python -m uvicorn app:app --host 127.0.0.1 --port 8000
# 4. open in a browser
# http://127.0.0.1:8000
```
## Demo accounts
| Username | Password | Role |
|---|---|---|
| `arao` | `rm123` | RM (mid-performer) |
| `kmehta` | `rm123` | RM |
| `mnair` | `rm123` | RM (top-tier book) |
| `msharma` | `manager123` | Manager (team view) |
| `kreddy` | `manager123` | Manager |
Other RM usernames are first-initial + surname, lowercased (printed when you run
`seed_data.py` if you inspect the DB). All RMs use `rm123`.
**Try this to see the security in action:** log in as one RM, note the clients
shown, sign out, log in as another โ completely different book. Then open browser
dev-tools network tab: the server only ever sends that RM's rows.
---
## Publish it on the web (Render โ free tier)
To let others see it work live, deploy to **Render**. The repo includes
`render.yaml`, `requirements.txt`, and an auto-build step, so it's nearly one-click.
1. Push this folder to a **GitHub** repo.
2. In Render: **New + โ Blueprint**, point it at the repo. Render reads
`render.yaml` and provisions a free web service.
3. Wait for the build (installs deps, builds the dummy DB), then open the public URL.
The app auto-builds the database on first start and serves over HTTPS. The
`HTTPS_DEPLOY=true` env var (set in `render.yaml`) turns on secure cookies.
**Keep it to one instance** (the blueprint already sets `numInstances: 1`).
Sessions and SQLite are in-process, so scaling out would break login.
> **Public-demo reminder:** this is a *functional* showcase with fake data and
> shared demo logins (shown on the login screen, now behind a "Demo mode" banner).
> The real system is **never** public โ it runs on-prem behind your VPN with AD.
> A public demo should not be treated as a hosting precedent.
### Other quick options
- **Temporary tunnel:** run locally, then `ngrok http 8000` for an instant public
URL that dies when you close it โ good for a single meeting.
- **Railway / Fly.io:** same idea as Render; the `requirements.txt` and start
command (`uvicorn app:app --host 0.0.0.0 --port $PORT`) work there too.
---
```bash
python test_poc.py
```
Runs 8 checks: unauthenticated requests blocked, bad credentials rejected, each RM
scoped to their own book, **zero client overlap between RMs**, RMs can't reach the
manager endpoint, managers see aggregates only (no client PII), audit logging
works, and the **GenAI brief is entitlement-checked + de-identified** (no name in
the external payload, others' clients blocked).
---
## Files
- `seed_data.py` โ generates the dummy database
- `app.py` โ FastAPI backend (auth, scoped data API, audit)
- `static/index.html` โ login screen + dashboard front-end
- `test_poc.py` โ security & functionality test suite
- `requirements.txt` โ Python dependencies for deployment
- `render.yaml` โ Render Blueprint for one-click web deploy
- `rm_oneview.db` โ the generated SQLite database (after step 2, or auto-built on deploy)
---
## Important caveats (this is a POC)
- The password hashing here is a toy (`sha256`); production uses AD โ the app
never stores or checks passwords itself.
- Sessions are in-memory and reset on restart.
- Data is randomly generated dummy data โ no real clients.
- Not hardened for deployment (no rate-limiting, HTTPS, CSRF tokens, etc.) โ those
come with the real on-prem hosting + AD integration.
The point of the POC is to prove the **shape** of the system โ especially that
data scoping is enforced server-side โ not to be production-ready.