saltcorn-samba 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/filemanager-view.js +249 -0
- package/index.js +622 -0
- package/package.json +56 -0
- package/pdf-view.js +163 -0
- package/public/samba-filemanager.js +736 -0
- package/public/samba-tree.js +292 -0
- package/public/samba.css +69 -0
- package/smb-client.js +279 -0
- package/tree-view.js +212 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `saltcorn-samba` are documented here.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] – 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Upload** von Dateien über den `SambaFileManager` — Multi-File-Auswahl,
|
|
11
|
+
Drag-&-Drop-Zone, optionales Überschreiben.
|
|
12
|
+
- **Neuer Ordner** anlegen direkt aus der Toolbar.
|
|
13
|
+
- **Umbenennen** und **Löschen** pro Zeile (Datei oder Ordner; Ordner werden
|
|
14
|
+
rekursiv gelöscht).
|
|
15
|
+
- Neue Plugin-Config-Felder (zweiter Config-Schritt *Access & permissions*):
|
|
16
|
+
- `min_role_write` (Default `40` = Staff+Admin, `100` deaktiviert alle
|
|
17
|
+
Schreibaktionen).
|
|
18
|
+
- `allow_upload`, `allow_delete`, `allow_rename`, `allow_mkdir`
|
|
19
|
+
(jeweils per-Feature einzeln aktivierbar, Default aus).
|
|
20
|
+
- `max_upload_mb` (Default `50`).
|
|
21
|
+
- `denied_extensions` (Blocklist; Default:
|
|
22
|
+
`exe,bat,cmd,com,msi,scr,vbs,js,jse,wsf,wsh,ps1,ps1xml,psm1,sh,bash,zsh`).
|
|
23
|
+
- Neue Routen: `POST /sambaupload`, `POST /sambadelete`, `POST /sambarename`,
|
|
24
|
+
`POST /sambamkdir`.
|
|
25
|
+
- `sanitizeFilename()` — lehnt Slashes, Steuerzeichen, `<>:"|?*`, führende/
|
|
26
|
+
abschließende Punkte oder Leerzeichen, Windows-Reserved-Names (CON, PRN,
|
|
27
|
+
AUX, NUL, COM1–9, LPT1–9) und Namen länger als 255 Zeichen ab.
|
|
28
|
+
- `SambaFileManager`-UI: Toolbar-Buttons *Upload*, *Neuer Ordner*, pro Zeile
|
|
29
|
+
*Umbenennen* / *Löschen*, Modal-Dialoge, Toast-Benachrichtigungen.
|
|
30
|
+
- Erweiterte Sanitizer-Tests (Filenames + Pfade, ~25 Testfälle).
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Plugin-Konfiguration ist jetzt zweistufig: *Samba server* und
|
|
34
|
+
*Access & permissions*. Bestehende `min_role_read`/Server-Felder bleiben
|
|
35
|
+
unverändert.
|
|
36
|
+
- `GET /sambadir` liefert zusätzlich ein `perms`-Objekt (`upload`,
|
|
37
|
+
`delete`, `rename`, `mkdir`), damit die UI Buttons für unberechtigte
|
|
38
|
+
Rollen ausblendet.
|
|
39
|
+
- README erweitert um alle neuen Config-Felder, Routen und UI-Aktionen.
|
|
40
|
+
|
|
41
|
+
### Security
|
|
42
|
+
- **CSRF-Schutz** auf allen neuen POST-Routen — akzeptiert entweder
|
|
43
|
+
`_csrf` im Body oder Header `X-CSRF-Token` / `CSRF-Token`.
|
|
44
|
+
- Path-Traversal-Prüfung greift auch für Ziel-Namen bei `rename` und `mkdir`.
|
|
45
|
+
- Extension-Blocklist verhindert das Hochladen ausführbarer Dateien.
|
|
46
|
+
- Rollen-Gate `min_role_write` schützt alle Schreiboperationen.
|
|
47
|
+
|
|
48
|
+
## [0.2.0] – 2026-07-05
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- **New view template `SambaFileManager`** — a Saltcorn-style file browser
|
|
52
|
+
(like *Settings → Files*). Table with icon, filename, media type, size,
|
|
53
|
+
modified date and per-row actions (View / Download / Open in file manager).
|
|
54
|
+
Includes breadcrumb navigation, up-button, home-button, refresh, hidden-file
|
|
55
|
+
toggle, click-to-sort columns and optional pagination.
|
|
56
|
+
- Inline PDF/image viewer directly inside the file-manager view.
|
|
57
|
+
- Publishing metadata: `repository`, `homepage`, `bugs`, `engines`,
|
|
58
|
+
`publishConfig`, `files`, `prepublishOnly` — ready for `npm publish` and
|
|
59
|
+
GitHub-based installs.
|
|
60
|
+
- MIT `LICENSE` file, `CHANGELOG.md`, `.gitignore`, `.npmignore`.
|
|
61
|
+
- Simple sanitizer unit tests under `test/`.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
- Plugin version is now read from `package.json`; the client bootstrap URLs
|
|
65
|
+
(`/plugins/public/saltcorn-samba@<version>/…`) update automatically.
|
|
66
|
+
- View templates are wrapped in `index.js` to inject `__pluginVersion` into
|
|
67
|
+
their configuration – no more hard-coded version strings across files.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
- Path sanitizer now rejects `//srv/share`-style UNC paths *before*
|
|
71
|
+
collapsing consecutive slashes.
|
|
72
|
+
- HTML-escape user-visible path in the `/sambalink` response.
|
|
73
|
+
|
|
74
|
+
## [0.1.0] – 2026-07-05
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- Initial release.
|
|
78
|
+
- `SambaTree` view template (lazy directory tree).
|
|
79
|
+
- `samba_pdf` fieldview (inline PDF/image viewer for String fields).
|
|
80
|
+
- Routes `/sambadir`, `/sambafile`, `/sambalink`.
|
|
81
|
+
- Direct SMB2 access via `@marsaud/smb2`.
|
|
82
|
+
- Strict path-traversal protection.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peter Vassen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# saltcorn-samba
|
|
2
|
+
|
|
3
|
+
Ein Saltcorn-Plugin für den Zugriff auf einen **Samba-/CIFS-Server (SMB2/3)**
|
|
4
|
+
direkt aus dem Browser — ohne Mount, ohne `smbclient` im Container.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
|
|
8
|
+
- 📂 **`SambaFileManager`-View** — Datei-Manager wie *Einstellungen → Dateien* in Saltcorn: Tabelle mit Symbol, Name, MIME-Typ, Größe, Änderungsdatum und Aktionen. Breadcrumb-Navigation, Up-/Home-/Refresh-Button, Sortierung per Spaltenklick, Pagination, Anzeige versteckter Dateien optional.
|
|
9
|
+
- ⬆️ **Upload / Neuer Ordner / Umbenennen / Löschen** — optional aktivierbar,
|
|
10
|
+
Multi-File-Upload mit Drag-&-Drop, Toast-Feedback, Rollen-Gate.
|
|
11
|
+
- 🌳 **`SambaTree`-View** — Lazy-loading Verzeichnisbaum, ideal zum Einbetten in Show-Views.
|
|
12
|
+
- 📄 **`samba_pdf`-Fieldview** — String-Feld mit Datei-Pfad → PDF/Bild inline im Browser, plus Download- und externe-App-Buttons.
|
|
13
|
+
- 🚀 **`smb://`-Links** — Öffnet Dateien und Ordner direkt in Nemo, Nautilus, Dolphin (Linux) oder Explorer (Windows).
|
|
14
|
+
- 🔒 **Sicherheit** — Base-Path als „Chroot", strenge Path-Traversal-Prüfung (`..`, absolute Pfade, UNC, Drive-Letters, NUL-Bytes), CSRF-Schutz auf Schreib-Routen, Filename-Sanitizer, Extension-Blocklist, rollenbasierter Zugriff (getrennte Lese-/Schreib-Rollen).
|
|
15
|
+
- 🐳 **Docker-freundlich** — direkt per SMB2-Protokoll, keine System-Binaries nötig.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Screenshots
|
|
20
|
+
|
|
21
|
+
Der `SambaFileManager` ist optisch am eingebauten Saltcorn-File-Browser
|
|
22
|
+
orientiert: dieselben Spalten (Filename, Media type, Size, Modified) und
|
|
23
|
+
Aktionsknöpfe.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
27
|
+
│ Samba files 3 folders · 12 files │
|
|
28
|
+
├─────────────────────────────────────────────────────────────────────┤
|
|
29
|
+
│ ⬆ Up 🏠 ↻ / kunden / akte-42 ☐ Show hidden │
|
|
30
|
+
├─────────────────────────────────────────────────────────────────────┤
|
|
31
|
+
│ Filename ▲ Media type Size Modified Actions │
|
|
32
|
+
│ 📁 vertraege folder 2026-06-01 [Open] │
|
|
33
|
+
│ 📁 rechnungen folder 2026-06-15 [Open] │
|
|
34
|
+
│ 📄 anschreiben.pdf application/pdf 84 KiB 2026-06-20 [View] [DL] ↗│
|
|
35
|
+
│ 🖼️ logo.png image/png 12 KiB 2026-05-11 [View] [DL] ↗│
|
|
36
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### 1. Über die Saltcorn-Plugin-Verwaltung (empfohlen)
|
|
44
|
+
|
|
45
|
+
Sobald das Plugin auf npm veröffentlicht ist:
|
|
46
|
+
|
|
47
|
+
1. Einstellungen → Plugins → „…" → *Add another plugin*
|
|
48
|
+
2. Name: `saltcorn-samba`
|
|
49
|
+
3. Source: **npm**
|
|
50
|
+
4. Location: `saltcorn-samba`
|
|
51
|
+
5. Version leer lassen (aktuellste) oder z. B. `0.3.0`
|
|
52
|
+
|
|
53
|
+
### 2. Direkt aus GitHub
|
|
54
|
+
|
|
55
|
+
1. Einstellungen → Plugins → „…" → *Add another plugin*
|
|
56
|
+
2. Name: `saltcorn-samba`
|
|
57
|
+
3. Source: **github**
|
|
58
|
+
4. Location: `pv-host/saltcorn-samba`
|
|
59
|
+
|
|
60
|
+
### 3. Lokale Installation (Entwicklung)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/pv-host/saltcorn-samba.git
|
|
64
|
+
cd saltcorn-samba
|
|
65
|
+
npm install
|
|
66
|
+
saltcorn install-plugin -d "$PWD"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Alternativ manuell via UI: Source = `local`, Location = absoluter Pfad.
|
|
70
|
+
|
|
71
|
+
### 4. Docker
|
|
72
|
+
|
|
73
|
+
Plugin-Ordner als Volume in den Container einbinden:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
services:
|
|
77
|
+
saltcorn:
|
|
78
|
+
image: saltcorn/saltcorn:latest
|
|
79
|
+
volumes:
|
|
80
|
+
- ./plugins/saltcorn-samba:/opt/plugins/saltcorn-samba
|
|
81
|
+
# Der SMB-Port des Samba-Servers muss aus dem Container erreichbar sein.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Anschließend in Saltcorn als *local* Plugin mit
|
|
85
|
+
`Location = /opt/plugins/saltcorn-samba` registrieren.
|
|
86
|
+
|
|
87
|
+
Verbindung testen:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
docker exec -it <saltcorn-container> \
|
|
91
|
+
node -e "require('net').createConnection(445,'IP-DES-SAMBA').on('connect',()=>{console.log('ok');process.exit()}).on('error',e=>{console.error(e.message);process.exit(1)})"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Konfiguration
|
|
97
|
+
|
|
98
|
+
Nach der Installation unter *Einstellungen → Plugins → saltcorn-samba →
|
|
99
|
+
Configure* ausfüllen. Die Konfiguration ist zweistufig:
|
|
100
|
+
|
|
101
|
+
### Schritt 1 — *Samba server*
|
|
102
|
+
|
|
103
|
+
| Feld | Beispiel | Beschreibung |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| Server | `192.168.1.10` | Hostname oder IP des Samba-Servers |
|
|
106
|
+
| Share name | `documents` | Name des Shares (ohne Slashes) |
|
|
107
|
+
| Domain / Workgroup | `WORKGROUP` | optional |
|
|
108
|
+
| Username | `saltcorn-user` | SMB-Benutzer |
|
|
109
|
+
| Password | *(secret)* | wird im Saltcorn-Konfigstore gespeichert |
|
|
110
|
+
| Base path | `projects` | *optional* — beschränkt jeden Zugriff auf dieses Unterverzeichnis |
|
|
111
|
+
| Port | `445` | Standard-SMB2-Port |
|
|
112
|
+
| SMB host visible to clients | `fileserver.lan` | *optional* — Host, der in `smb://`-Links auftaucht (nützlich in Docker) |
|
|
113
|
+
|
|
114
|
+
### Schritt 2 — *Access & permissions*
|
|
115
|
+
|
|
116
|
+
| Feld | Default | Beschreibung |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| Minimum role to read files | `80` | 1=Admin, 40=Staff, 80=User, 100=public |
|
|
119
|
+
| Minimum role to write files | `40` | `100` deaktiviert Schreibaktionen komplett |
|
|
120
|
+
| Allow upload | *aus* | schaltet Upload-Button + `POST /sambaupload` frei |
|
|
121
|
+
| Allow delete | *aus* | schaltet Löschen frei (`POST /sambadelete`) |
|
|
122
|
+
| Allow rename | *aus* | schaltet Umbenennen frei (`POST /sambarename`) |
|
|
123
|
+
| Allow mkdir | *aus* | schaltet *Neuer Ordner* frei (`POST /sambamkdir`) |
|
|
124
|
+
| Max. Upload-Größe (MB) | `50` | Limit pro Datei |
|
|
125
|
+
| Denied file extensions | `exe,bat,cmd,com,msi,scr,vbs,js,jse,wsf,wsh,ps1,ps1xml,psm1,sh,bash,zsh` | kommagetrennte Blocklist |
|
|
126
|
+
|
|
127
|
+
**Empfehlung:**
|
|
128
|
+
|
|
129
|
+
- Nur die Features aktivieren, die wirklich gebraucht werden.
|
|
130
|
+
- Auf dem Samba-Server einen separaten Nutzer mit passenden Rechten anlegen
|
|
131
|
+
(read-only wenn nur gelesen werden soll).
|
|
132
|
+
- Zusätzlich mit *Base path* den Zugriff auf ein Unterverzeichnis begrenzen.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Verwendung
|
|
137
|
+
|
|
138
|
+
### View: `SambaFileManager`
|
|
139
|
+
|
|
140
|
+
Menu → Views → *Create view* → Template `SambaFileManager`.
|
|
141
|
+
|
|
142
|
+
Optionen:
|
|
143
|
+
|
|
144
|
+
| Option | Zweck |
|
|
145
|
+
|---|---|
|
|
146
|
+
| **Root directory mode** | `static` = immer Base-Path aus Plugin-Config, `from_field` = Sub-Pfad kommt aus einem DB-Feld der aktuellen Zeile |
|
|
147
|
+
| **Row field with sub-path** | Feld der Tabelle mit dem relativen Ordnernamen (bei `from_field`) |
|
|
148
|
+
| **Extra sub-path** | statischer Suffix (z. B. `invoices`) |
|
|
149
|
+
| **Show hidden files** | `.dotfiles` einblenden |
|
|
150
|
+
| **Allow navigating up** | Up-Button aktivieren (nur bis zum Root, kein Ausbruch) |
|
|
151
|
+
| **Open PDFs / images inline** | Klick öffnet die Datei im integrierten Viewer |
|
|
152
|
+
| **Show "Open in file manager" button** | `smb://`-Link pro Zeile |
|
|
153
|
+
| **Page size** | Einträge pro Seite (0 = alle) |
|
|
154
|
+
| **Panel title** | Text im Karten-Header |
|
|
155
|
+
|
|
156
|
+
Beispiel-Datenmodell für kunden-spezifische Ordner:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
Tabelle: kunden
|
|
160
|
+
Felder: name (String), akte_dir (String, z. B. "kunde_42/2026")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Dann eine Show-View von `kunden` bauen und die `SambaFileManager`-View mit
|
|
164
|
+
Mode = `from_field`, Row field = `akte_dir` einbetten.
|
|
165
|
+
|
|
166
|
+
### View: `SambaTree`
|
|
167
|
+
|
|
168
|
+
Kompaktere Alternative – lazy-loading Baum, ideal in einer Sidebar oder
|
|
169
|
+
neben Formularen. Gleiche Path-Modi wie beim File-Manager.
|
|
170
|
+
|
|
171
|
+
### Fieldview `samba_pdf`
|
|
172
|
+
|
|
173
|
+
Für ein String-Feld mit Datei-Pfad (relativ zum Base-Path). In der View-
|
|
174
|
+
Konfiguration `Field view = samba_pdf` wählen. Rendert PDFs im `<iframe>`,
|
|
175
|
+
Bilder als `<img>`, alles andere als Download-Buttons.
|
|
176
|
+
|
|
177
|
+
### smb://-Links
|
|
178
|
+
|
|
179
|
+
Jeder Datei-/Ordner-Eintrag hat einen ↗-Button. Klick öffnet die Route
|
|
180
|
+
`/sambalink`, die eine kleine HTML-Zwischenseite mit `smb://`-Anker rendert.
|
|
181
|
+
|
|
182
|
+
- Linux: GNOME/Cinnamon/KDE → Nautilus / Nemo / Dolphin
|
|
183
|
+
- Windows: Explorer
|
|
184
|
+
- macOS: Finder
|
|
185
|
+
|
|
186
|
+
*Hinweis:* Chromium blockiert direkte `smb://`-Redirects, deshalb die
|
|
187
|
+
Zwischenseite mit sichtbarem Klick-Link.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Routen (öffentliche HTTP-API des Plugins)
|
|
192
|
+
|
|
193
|
+
### Lesen (GET, Rolle ≥ `min_role_read`)
|
|
194
|
+
|
|
195
|
+
| Methode + URL | Parameter | Zweck |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| `GET /sambadir` | `path`, `show_hidden` | JSON-Verzeichnisliste + `perms` |
|
|
198
|
+
| `GET /sambafile` | `path`, `disposition=inline\|attachment` | Datei-Stream |
|
|
199
|
+
| `GET /sambalink` | `path` | HTML-Seite mit `smb://`-Anker |
|
|
200
|
+
|
|
201
|
+
### Schreiben (POST, Rolle ≥ `min_role_write`, CSRF-Token erforderlich)
|
|
202
|
+
|
|
203
|
+
| Methode + URL | Body / Form | Zweck |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| `POST /sambaupload` | `multipart/form-data`: `dir`, `file` (n-fach), `overwrite`, `_csrf` | Dateien hochladen |
|
|
206
|
+
| `POST /sambadelete` | JSON: `path`, `_csrf` | Datei/Ordner (rekursiv) löschen |
|
|
207
|
+
| `POST /sambarename` | JSON: `path`, `new_name`, `_csrf` | Umbenennen |
|
|
208
|
+
| `POST /sambamkdir` | JSON: `dir`, `name`, `_csrf` | Neuen Ordner anlegen |
|
|
209
|
+
|
|
210
|
+
Das CSRF-Token wird von Saltcorn per `req.csrfToken()` erzeugt. Der Client
|
|
211
|
+
übergibt es entweder als Feld `_csrf` im Body oder als HTTP-Header
|
|
212
|
+
`X-CSRF-Token` (bzw. `CSRF-Token`). Wer CSRF für diese Routen abschalten
|
|
213
|
+
möchte, kann das in den Saltcorn-*Users & Security*-Einstellungen tun.
|
|
214
|
+
|
|
215
|
+
Alle Routen prüfen die Rolle, validieren jeden Pfad gegen Path-Traversal
|
|
216
|
+
und lehnen Filenames ab, die Slashes, Steuerzeichen, `<>:"|?*`,
|
|
217
|
+
führende/abschließende Punkte oder Windows-Reserved-Names enthalten.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Sicherheit
|
|
222
|
+
|
|
223
|
+
- **Base-Path als Chroot** — sanitizer verhindert das Verlassen.
|
|
224
|
+
- **Path-Prüfung** — `..`, absolute Pfade (`/foo`), UNC (`//srv/share`,
|
|
225
|
+
`\\srv\share`), Drive-Letters (`C:`, `d:\`), NUL-Bytes werden abgelehnt.
|
|
226
|
+
- **Filename-Sanitizer** für Upload / Rename / Mkdir — lehnt Slashes,
|
|
227
|
+
Steuerzeichen, `<>:"|?*`, führende/abschließende Punkte, Leerzeichen und
|
|
228
|
+
Windows-Reserved-Names (`CON`, `PRN`, `AUX`, `NUL`, `COM1–9`, `LPT1–9`)
|
|
229
|
+
ab.
|
|
230
|
+
- **CSRF-Schutz** auf allen POST-Routen (Body `_csrf` oder Header
|
|
231
|
+
`X-CSRF-Token`).
|
|
232
|
+
- **Extension-Blocklist** für Uploads (Default blockt exe/bat/cmd/vbs/js
|
|
233
|
+
u.ä.), konfigurierbar.
|
|
234
|
+
- **Getrennte Rollen-Gates** — `min_role_read` und `min_role_write`, plus
|
|
235
|
+
Feature-Toggles pro Schreib-Aktion.
|
|
236
|
+
- **Kein öffentliches Caching** — Files werden mit `Cache-Control: no-store`
|
|
237
|
+
ausgeliefert.
|
|
238
|
+
- **HTML-Escape** in allen serverseitig gerenderten Antworten.
|
|
239
|
+
|
|
240
|
+
Unit-Tests zum Path- und Filename-Sanitizer laufen in CI (`npm test`).
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Entwicklung
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npm install
|
|
248
|
+
npm run lint # Syntax-Check aller JS-Dateien
|
|
249
|
+
npm test # Sanitizer-Tests
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Lokale Saltcorn-Instanz mit Live-Reload:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
saltcorn install-plugin -d "$PWD"
|
|
256
|
+
SALTCORN_NWORKERS=1 saltcorn serve --dev
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Anschließend Änderungen an `index.js`/`filemanager-view.js`/etc. → Server
|
|
260
|
+
lädt neu.
|
|
261
|
+
|
|
262
|
+
### Als npm-Paket veröffentlichen
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# In package.json: version bumpen, CHANGELOG ergänzen
|
|
266
|
+
npm login
|
|
267
|
+
npm run lint && npm test # läuft auch als prepublishOnly
|
|
268
|
+
npm publish # --access public wird durch publishConfig gesetzt
|
|
269
|
+
git tag v0.3.0 && git push --tags
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Als GitHub-Plugin nutzbar machen
|
|
273
|
+
|
|
274
|
+
Nach `git push` auf `main`/`master` können Saltcorn-Nutzer das Plugin ohne
|
|
275
|
+
npm direkt installieren:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
Source: github
|
|
279
|
+
Location: pv-host/saltcorn-samba
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Saltcorn führt `npm install` im Plugin-Ordner automatisch aus.
|
|
283
|
+
|
|
284
|
+
### Ins Saltcorn-Store-Verzeichnis eintragen
|
|
285
|
+
|
|
286
|
+
1. Auf https://store.saltcorn.com/ einloggen (Admin-Rolle nötig)
|
|
287
|
+
2. „Add extension" → Name `saltcorn-samba`, Source `npm`, Location `saltcorn-samba`
|
|
288
|
+
3. Beschreibung + Kategorie eintragen
|
|
289
|
+
|
|
290
|
+
Ab dann taucht das Plugin im Plugins-Store jeder Saltcorn-Instanz auf.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Bekannte Grenzen
|
|
295
|
+
|
|
296
|
+
- Dateien werden komplett gepuffert (`readFile` / `writeFile`). Für
|
|
297
|
+
Dateien > 100 MB besser einen CIFS-Mount + Saltcorn-`File`-Typ nutzen.
|
|
298
|
+
- **Nur SMB2/3** — SMBv1 wird nicht unterstützt.
|
|
299
|
+
- **Ein SMB-User** entscheidet über Sichtbarkeit und Schreibrechte auf
|
|
300
|
+
dem Share — für rollenspezifische Sichtbarkeit auf Share-Ebene
|
|
301
|
+
arbeiten oder mehrere Plugin-Instanzen verwenden.
|
|
302
|
+
- **Keine DB-Verknüpfung** von SMB-Dateien (geplant für spätere Version).
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Changelog
|
|
307
|
+
|
|
308
|
+
Siehe [CHANGELOG.md](CHANGELOG.md).
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Lizenz
|
|
313
|
+
|
|
314
|
+
MIT © 2026 Peter Vassen
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View template: `SambaFileManager`
|
|
3
|
+
*
|
|
4
|
+
* Renders a Saltcorn-style file browser (like Settings → Files) against a
|
|
5
|
+
* Samba/CIFS share. The UI shows a table with columns: icon, name, media
|
|
6
|
+
* type, size, modified, actions. Directories are navigable via click, and
|
|
7
|
+
* the current location is shown as a breadcrumb path.
|
|
8
|
+
*
|
|
9
|
+
* All directory data is fetched lazily from `/sambadir` (defined in the
|
|
10
|
+
* plugin's routes) so the view itself just renders a shell that boots the
|
|
11
|
+
* client-side script.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { div, span, script } = require("@saltcorn/markup/tags");
|
|
15
|
+
const Workflow = require("@saltcorn/data/models/workflow");
|
|
16
|
+
const Form = require("@saltcorn/data/models/form");
|
|
17
|
+
const Field = require("@saltcorn/data/models/field");
|
|
18
|
+
const Table = require("@saltcorn/data/models/table");
|
|
19
|
+
|
|
20
|
+
const { sanitizeRelativePath } = require("./smb-client");
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Configuration workflow
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const configuration_workflow = () =>
|
|
27
|
+
new Workflow({
|
|
28
|
+
steps: [
|
|
29
|
+
{
|
|
30
|
+
name: "File manager options",
|
|
31
|
+
form: async (context) => {
|
|
32
|
+
const table = context.table_id
|
|
33
|
+
? await Table.findOne({ id: context.table_id })
|
|
34
|
+
: null;
|
|
35
|
+
const fields = table ? await table.getFields() : [];
|
|
36
|
+
const stringFieldOptions = fields
|
|
37
|
+
.filter(
|
|
38
|
+
(f) =>
|
|
39
|
+
f.type &&
|
|
40
|
+
(f.type.name === "String" ||
|
|
41
|
+
f.type === "String" ||
|
|
42
|
+
f.type.name === "Text")
|
|
43
|
+
)
|
|
44
|
+
.map((f) => f.name);
|
|
45
|
+
return new Form({
|
|
46
|
+
fields: [
|
|
47
|
+
new Field({
|
|
48
|
+
name: "mode",
|
|
49
|
+
label: "Root directory mode",
|
|
50
|
+
type: "String",
|
|
51
|
+
required: true,
|
|
52
|
+
attributes: {
|
|
53
|
+
options: ["static", "from_field"],
|
|
54
|
+
},
|
|
55
|
+
sublabel:
|
|
56
|
+
"static = always the plugin base_path. from_field = append a row field value to base_path (only when the view is embedded in a Show view).",
|
|
57
|
+
default: "static",
|
|
58
|
+
}),
|
|
59
|
+
new Field({
|
|
60
|
+
name: "path_field",
|
|
61
|
+
label: "Row field with sub-path",
|
|
62
|
+
type: "String",
|
|
63
|
+
attributes: { options: stringFieldOptions },
|
|
64
|
+
showIf: { mode: "from_field" },
|
|
65
|
+
}),
|
|
66
|
+
new Field({
|
|
67
|
+
name: "extra_subpath",
|
|
68
|
+
label: "Extra sub-path (appended)",
|
|
69
|
+
type: "String",
|
|
70
|
+
sublabel:
|
|
71
|
+
"Optional. Static suffix appended after the field value. Example: 'invoices'",
|
|
72
|
+
}),
|
|
73
|
+
new Field({
|
|
74
|
+
name: "show_hidden",
|
|
75
|
+
label: "Show hidden files",
|
|
76
|
+
type: "Bool",
|
|
77
|
+
}),
|
|
78
|
+
new Field({
|
|
79
|
+
name: "allow_navigate_up",
|
|
80
|
+
label: "Allow navigating up (until the root)",
|
|
81
|
+
type: "Bool",
|
|
82
|
+
default: true,
|
|
83
|
+
}),
|
|
84
|
+
new Field({
|
|
85
|
+
name: "pdf_inline",
|
|
86
|
+
label: "Open PDFs / images inline",
|
|
87
|
+
type: "Bool",
|
|
88
|
+
default: true,
|
|
89
|
+
}),
|
|
90
|
+
new Field({
|
|
91
|
+
name: "expose_smb_link",
|
|
92
|
+
label: "Show 'Open in file manager' button (smb://)",
|
|
93
|
+
type: "Bool",
|
|
94
|
+
default: true,
|
|
95
|
+
}),
|
|
96
|
+
new Field({
|
|
97
|
+
name: "page_size",
|
|
98
|
+
label: "Page size",
|
|
99
|
+
type: "Integer",
|
|
100
|
+
default: 100,
|
|
101
|
+
sublabel:
|
|
102
|
+
"How many entries to show per page. 0 = show all (be careful with huge directories).",
|
|
103
|
+
}),
|
|
104
|
+
new Field({
|
|
105
|
+
name: "title",
|
|
106
|
+
label: "Panel title",
|
|
107
|
+
type: "String",
|
|
108
|
+
default: "Samba files",
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Helpers
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
async function get_state_fields() {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function computeStartPath(configuration, row) {
|
|
126
|
+
const parts = [];
|
|
127
|
+
if (configuration.mode === "from_field" && configuration.path_field && row) {
|
|
128
|
+
const v = row[configuration.path_field];
|
|
129
|
+
if (v) parts.push(String(v));
|
|
130
|
+
}
|
|
131
|
+
if (configuration.extra_subpath) parts.push(String(configuration.extra_subpath));
|
|
132
|
+
return sanitizeRelativePath(parts.join("/"));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Shell renderer
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
function renderShell(startPath, configuration, pluginVersion, csrfToken) {
|
|
140
|
+
const id = "samba-fm-" + Math.random().toString(36).slice(2, 10);
|
|
141
|
+
const opts = {
|
|
142
|
+
startPath,
|
|
143
|
+
showHidden: !!configuration.show_hidden,
|
|
144
|
+
allowNavigateUp: configuration.allow_navigate_up !== false,
|
|
145
|
+
pdfInline: configuration.pdf_inline !== false,
|
|
146
|
+
exposeSmbLink: configuration.expose_smb_link !== false,
|
|
147
|
+
pageSize: Number(configuration.page_size) || 0,
|
|
148
|
+
title: configuration.title || "Samba files",
|
|
149
|
+
csrfToken: csrfToken || "",
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
div(
|
|
154
|
+
{ class: "samba-fm card" },
|
|
155
|
+
div(
|
|
156
|
+
{ class: "card-header d-flex align-items-center" },
|
|
157
|
+
span({ class: "samba-fm-title fw-bold" }, opts.title),
|
|
158
|
+
span({ class: "samba-fm-spacer flex-grow-1" }),
|
|
159
|
+
span({ id: id + "-count", class: "text-muted small" })
|
|
160
|
+
),
|
|
161
|
+
div(
|
|
162
|
+
{ class: "card-body p-0" },
|
|
163
|
+
div({ id: id + "-toolbar", class: "samba-fm-toolbar p-2 border-bottom" }),
|
|
164
|
+
div({ id: id + "-list", class: "samba-fm-list" }),
|
|
165
|
+
div({ id: id + "-viewer", class: "samba-fm-viewer" })
|
|
166
|
+
)
|
|
167
|
+
) +
|
|
168
|
+
script(
|
|
169
|
+
`(function(){
|
|
170
|
+
function boot(){window.saltcornSambaMountFM && window.saltcornSambaMountFM(${JSON.stringify(
|
|
171
|
+
id
|
|
172
|
+
)}, ${JSON.stringify(opts)});}
|
|
173
|
+
if(window.saltcornSambaMountFM){boot();return;}
|
|
174
|
+
var s=document.createElement('script');
|
|
175
|
+
s.src='/plugins/public/saltcorn-samba@${pluginVersion}/samba-filemanager.js';
|
|
176
|
+
s.onload=boot;
|
|
177
|
+
document.head.appendChild(s);
|
|
178
|
+
})();`
|
|
179
|
+
)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// View functions
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function extractCsrf(extra) {
|
|
188
|
+
const req = extra && extra.req;
|
|
189
|
+
if (req && typeof req.csrfToken === "function") {
|
|
190
|
+
try {
|
|
191
|
+
return req.csrfToken();
|
|
192
|
+
} catch (_) {
|
|
193
|
+
return "";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function run(table_id, viewname, configuration, state, extra) {
|
|
200
|
+
const pluginVersion = configuration.__pluginVersion || "0.3.0";
|
|
201
|
+
const csrf = extractCsrf(extra);
|
|
202
|
+
let row = extra && extra.row;
|
|
203
|
+
if (!row && configuration.mode === "from_field" && table_id) {
|
|
204
|
+
const table = await Table.findOne({ id: table_id });
|
|
205
|
+
if (table && state && Object.keys(state).length) {
|
|
206
|
+
try {
|
|
207
|
+
row = await table.getRow(state);
|
|
208
|
+
} catch (_) {
|
|
209
|
+
row = null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
let startPath = "";
|
|
214
|
+
try {
|
|
215
|
+
startPath = computeStartPath(configuration, row || {});
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return div({ class: "alert alert-danger" }, "Samba file manager: " + e.message);
|
|
218
|
+
}
|
|
219
|
+
return renderShell(startPath, configuration, pluginVersion, csrf);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function runMany(table_id, viewname, configuration, state, extra) {
|
|
223
|
+
const pluginVersion = configuration.__pluginVersion || "0.3.0";
|
|
224
|
+
const csrf = extractCsrf(extra);
|
|
225
|
+
const rows = (extra && extra.rows) || [];
|
|
226
|
+
return rows.map((row) => {
|
|
227
|
+
let startPath = "";
|
|
228
|
+
try {
|
|
229
|
+
startPath = computeStartPath(configuration, row);
|
|
230
|
+
} catch (_) {
|
|
231
|
+
startPath = "";
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
html: renderShell(startPath, configuration, pluginVersion, csrf),
|
|
235
|
+
row,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = {
|
|
241
|
+
name: "SambaFileManager",
|
|
242
|
+
display_state_form: false,
|
|
243
|
+
configuration_workflow,
|
|
244
|
+
get_state_fields,
|
|
245
|
+
run,
|
|
246
|
+
runMany,
|
|
247
|
+
description:
|
|
248
|
+
"Saltcorn-style file browser for a Samba/CIFS share. Table with icon, name, type, size, modified date, and actions.",
|
|
249
|
+
};
|