170 lines
11 KiB
Markdown
170 lines
11 KiB
Markdown
# Plan — Mailbox Permission Scanning
|
|
|
|
Voorstel voor het uitbreiden van Clearview met mailbox-rechten scanning naast de bestaande SharePoint-scan. Status: **voorstel, nog niet geïmplementeerd**.
|
|
|
|
## 1. Scope & aanpak
|
|
|
|
In Exchange Online zijn dit de relevante permissie-categorieën:
|
|
|
|
| Permissie | Bron | Waarop |
|
|
|---|---|---|
|
|
| **Full Access / Read** | `Get-MailboxPermission` | hele mailbox |
|
|
| **Send As** | `Get-RecipientPermission` | identiteit |
|
|
| **Send on Behalf** | mailbox-property `GrantSendOnBehalfTo` | identiteit |
|
|
| **Folder delegaties** (Calendar, Inbox) | `Get-MailboxFolderPermission` | per folder |
|
|
|
|
Microsoft Graph dekt dit slechts gedeeltelijk (vooral folder-permissies). De volledige set vereist **Exchange Online PowerShell met certificaat-auth** (`Connect-ExchangeOnline -CertificateThumbprint ... -AppId ... -Organization ...`). Dat sluit goed aan op het bestaande cert-model van Clearview — dezelfde Azure-app kan zowel `Sites.FullControl.All` (SharePoint) als `Exchange.ManageAsApp` (Exchange) krijgen.
|
|
|
|
**Voorgestelde aanpak:** EXO PowerShell aanroepen vanuit Python via `pwsh` subprocess, JSON-output retour. Alternatief is pure Graph, maar dan mist Send As / Send on Behalf grotendeels.
|
|
|
|
**Deviation-baseline voor mailboxen:** anders dan SharePoint heeft een mailbox geen "root vs. child" hiërarchie waar een baseline uit volgt. Voorstel: rapporteer **alle non-default permissies** (filter standaard-entries als `NT AUTHORITY\SELF`, `S-1-5-10`, default folder-permissies "Default"/"Anonymous=None") als deviation. Dus géén delta-vergelijk, maar wel dezelfde dedup-logica per (principal, role).
|
|
|
|
## 2. Layout-wijzigingen frontend
|
|
|
|
Huidige website is volledig op SharePoint gemodelleerd (één scanflow, één resultaattabel) en bestaat uit één lange single-page met alle panels (Tenants, Start New Scan, Scan Jobs, Job Details) onder elkaar. Voorstel: opsplitsen in pagina's met een **vaste linker zijbalk** zoals AlertHub gebruikt.
|
|
|
|
**a. Sidebar-layout (geïnspireerd op AlertHub)**
|
|
|
|
Vaste zijbalk links (~200px, donkere achtergrond, logo bovenaan, account-blok onderaan):
|
|
|
|
```
|
|
┌────────────┬──────────────────────────────────┐
|
|
│ Clearview │ │
|
|
│ ───────── │ (page content) │
|
|
│ Dashboard │ │
|
|
│ Scan Jobs │ │
|
|
│ ───────── │ │
|
|
│ SharePoint │ │
|
|
│ New Scan │ │
|
|
│ Mailboxes │ │
|
|
│ New Scan │ │
|
|
│ ───────── │ │
|
|
│ Tenants │ │
|
|
│ Settings │ │
|
|
│ ───────── │ │
|
|
│ Manual │ │
|
|
│ Changelog │ │
|
|
│ ───────── │ │
|
|
│ [account] │ │
|
|
└────────────┴──────────────────────────────────┘
|
|
```
|
|
|
|
Items:
|
|
- **Dashboard** — hero/KPI's (Tenants, Jobs, Active Jobs) — huidige `hero` sectie
|
|
- **Scan Jobs** — gecombineerde lijst van alle jobs (filter op type/tenant) + Job Details als detail-view (klik op job → toont onder lijst óf als aparte route)
|
|
- **SharePoint → New Scan** — formulier alleen voor SharePoint-scans (URLs / CSV)
|
|
- **Mailboxes → New Scan** — formulier alleen voor mailbox-scans (UPNs / CSV)
|
|
- **Tenants** — huidige tenants-panel
|
|
- **Settings** — placeholder voor toekomstige instellingen (timeouts, env-overrides als die exposed worden)
|
|
- **Manual** / **Changelog** — statische docs als die online beschikbaar moeten komen
|
|
- Account-blok onderaan: huidige sessie / build-versie
|
|
|
|
Implementatie blijft **vanilla HTML/JS/CSS** (geen React-introductie — dat zou een grote scope-uitbreiding zijn). Concreet:
|
|
- `index.html` houdt alle secties als `<section data-route="...">`, default verborgen
|
|
- Hash-based routing in `app.js` (`#/jobs`, `#/scan/sharepoint`, `#/scan/mailbox`, `#/tenants`, …)
|
|
- `styles.css` krijgt grid-layout met sidebar + content area
|
|
- Geen page reloads; navigatie wisselt zichtbaarheid van secties en update active-state in de sidebar
|
|
|
|
Voordelen tegenover de huidige single-scroll:
|
|
- Scan-typen visueel gescheiden — voorkomt verwarring tussen SharePoint- en Mailbox-formulieren
|
|
- Schaalt naar meer scan-types (OneDrive, Teams) zonder dat de pagina nóg langer wordt
|
|
- Visuele consistentie met AlertHub binnen jouw productenfamilie
|
|
|
|
**b. Scan-formulier per type (gescheiden pagina's)**
|
|
- SharePoint-pagina: huidige URLs/CSV-formulier, ongewijzigd qua velden
|
|
- Mailbox-pagina: invoerveld voor UPN/email (één per regel), géén "skip default sites"
|
|
- CSV-import per pagina, met geschikte kolomdetectie (`URL`/`Site URL` versus `UserPrincipalName`/`Email`/`Mailbox`)
|
|
|
|
**c. Jobs-pagina**
|
|
- Gecombineerde tabel met alle jobs ongeacht type
|
|
- Nieuwe kolom **Type** (SharePoint / Mailbox)
|
|
- Tenant-filter blijft, extra filter op type
|
|
- Klik op een job opent de detail-view (sectie eronder of dedicated route `#/jobs/{id}`)
|
|
|
|
**d. Job Details (binnen Jobs-pagina)**
|
|
- Sectie heading wordt dynamisch: "Targets" blijft, maar URL-kolom wordt "Mailbox" voor mailbox-jobs
|
|
- Permission Deviations tabel krijgt voor mailbox-jobs andere kolommen:
|
|
- Mailbox · Folder (leeg voor mailbox-level) · Permission Type (FullAccess/SendAs/SendOnBehalf/Folder) · Principal · Access Rights
|
|
- SharingLinks-blok wordt verborgen voor mailbox-jobs
|
|
- Site-filter wordt mailbox-filter
|
|
|
|
**e. Tenants-pagina**
|
|
- In de onboarding-instructies een extra stap erbij: API permission `Office 365 Exchange Online → Exchange.ManageAsApp` + role `Exchange Administrator` toekennen aan de service principal. Voor de geautomatiseerde flow betekent dit een extra Graph-call (rol-toekenning kan niet altijd, dus mogelijk handmatige stap).
|
|
- Statuskolom `Auth` toont al cert/secret; voorstel: extra capability-badge per tenant (`SP`, `EXO`) zodat zichtbaar is welke scans mogelijk zijn.
|
|
|
|
## 3. Backend-optimalisatie / herstructurering
|
|
|
|
`scanner.py` is nu één bestand met SharePoint-specifieke logica én generieke helpers (auth, HTTP, dedup, role-name normalization). Voorstel:
|
|
|
|
```
|
|
src/clearview_app/scanners/
|
|
__init__.py # dispatcher: scan(target_type, ...)
|
|
common.py # AuthConfig, DeviationRecord, ScanResult, ProbeResult,
|
|
# MSAL token cache, _request_json, _iter_paged,
|
|
# _deduplicate_hierarchical, role-name normalization
|
|
sharepoint.py # huidige scan_site_for_deviations + probe_site + resolve_sharing_link_members
|
|
mailbox.py # nieuw: scan_mailbox_for_deviations + probe_mailbox
|
|
# roept pwsh aan met EXO-script en parseert JSON
|
|
exo_scripts/
|
|
get-permissions.ps1 # connect + get mailbox/recipient/folder permissions, output JSON
|
|
probe.ps1 # lichtgewicht: Get-EXOMailbox -Identity x | select Identity
|
|
```
|
|
|
|
Voordelen:
|
|
- `scanner.py` was richting 540 regels en mengt concerns; opsplitsen maakt testen makkelijker
|
|
- `worker.py` krijgt één dispatcher-call: `scan(target_type, target, auth, progress)` ipv. directe import van SharePoint-specifieke functies
|
|
- Toekomstige scan-types (OneDrive, Teams) passen in hetzelfde patroon
|
|
|
|
**Worker.py** moet leren: per target weten welke scanner aan te roepen op basis van `scan_targets.target_type` (of `scan_jobs.scan_type`).
|
|
|
|
## 4. Datamodel
|
|
|
|
Minimale, niet-brekende wijzigingen — toegepast via bestaand `_ensure_schema_columns()`:
|
|
|
|
| Tabel | Nieuwe kolom | Default |
|
|
|---|---|---|
|
|
| `scan_jobs` | `scan_type` VARCHAR(32) | `'sharepoint'` |
|
|
| `scan_targets` | (geen — `site_url` blijft, bevat UPN voor mailbox-jobs; of hernoem naar `target` met DB-view voor compat) | — |
|
|
| `permission_deviations` | `permission_type` VARCHAR(32) NULL | NULL (voor SP-rows) |
|
|
| `permission_deviations` | `object_type` krijgt nieuwe waarden: `Mailbox`, `MailboxFolder` | — |
|
|
|
|
**Vraag:** liever `site_url` houden als generieke "target identifier" (compacter, geen migratie), of nu hernoemen? Eerste optie is mijn voorstel.
|
|
|
|
## 5. API-endpoints
|
|
|
|
Bestaand blijft werken. Toevoegingen:
|
|
|
|
```
|
|
POST /api/scan-jobs payload krijgt optioneel "scan_type": "sharepoint"|"mailbox"
|
|
en "mailboxes": [...] naast/ipv "site_urls"
|
|
POST /api/scan-jobs/import-csv extra "scan_type" form-field
|
|
GET /api/scan-jobs?scan_type=mailbox filter
|
|
```
|
|
|
|
Nieuwe response-velden in `ScanJobDetail`: `scan_type`, en deviations krijgen `permission_type`.
|
|
|
|
## 6. Onboarding & runtime-vereisten
|
|
|
|
- **Container**: PowerShell 7 (`pwsh`) en de `ExchangeOnlineManagement` PS-module installeren in de Docker image. Dit voegt ~150MB toe. Acceptabel?
|
|
- **Azure-app**: extra permission `Office 365 Exchange Online → Exchange.ManageAsApp` + Entra-rol "Exchange Administrator" voor de service principal. In Mode A (automated) kan het permission-deel via Graph, het rol-deel meestal niet — handmatige bevestigingsstap nodig.
|
|
- **Probe**: voor mailboxen één kleine call (`Get-EXOMailbox -Identity <upn> -Properties Identity` via een korter PS-script) om vroeg te falen op auth/rol.
|
|
|
|
## 7. Implementatiestappen (volgorde)
|
|
|
|
1. Backend refactor: scanner-module opsplitsen, geen functionele wijziging (groen test)
|
|
2. Datamodel: `scan_type` + `permission_type` toevoegen, default `'sharepoint'`
|
|
3. Frontend layout-refactor: sidebar + hash-routing introduceren, bestaande functionaliteit (alleen SharePoint) verdelen over pagina's. Nog géén mailbox-UI.
|
|
4. Mailbox-scanner: PS-scripts + Python-wrapper + dispatcher; alleen via API testbaar
|
|
5. API: scan-job creatie uitbreiden met `scan_type`/`mailboxes`
|
|
6. Frontend: Mailboxes-pagina toevoegen + dynamische kolommen in Job Details
|
|
7. Onboarding-tekst en build (Dockerfile voor `pwsh`)
|
|
8. Documentatie: TECHNICAL.md + changelog-develop.md
|
|
|
|
## Open vragen
|
|
|
|
1. **PowerShell-route akkoord**, of liever Graph-only (incompleter, maar pure Python)?
|
|
2. **Eén scan-type per job** (voorstel), of mengbare jobs (SharePoint + mailboxen door elkaar)?
|
|
3. **Baseline voor mailbox**: alle non-default rechten als deviation rapporteren (voorstel), of moet er een "expected baseline" per mailbox/tenant configureerbaar worden?
|
|
4. **Image-grootte**: is +150 MB voor `pwsh` + EXO-module aanvaardbaar?
|
|
5. **`site_url` kolom hergebruiken** als generieke target-identifier, of nu hernoemen naar `target`?
|