saltcorn-samba 0.3.7 → 0.3.8

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/index.js +116 -24
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,37 @@ All notable changes to `saltcorn-samba` are documented here.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.3.8] – 2026-07-05
8
+
9
+ ### Changed
10
+ - **Admin-Erkennung im Test-Endpoint komplett neu aufgezogen.**
11
+ Statt weiter zu raten, welches Feld/welchen Typ Saltcorn für die Rolle
12
+ verwendet, wird jetzt aktiv nachgeladen. Drei unabhängige Wege, es
13
+ reicht wenn *einer* Erfolg hat:
14
+
15
+ 1. **DB-Lookup (primär):** Aus der Session eine Kandidaten-ID/E-Mail
16
+ lesen und mit `User.findOne({ id })` frisch aus der Saltcorn-
17
+ Datenbank laden. Der User-Konstruktor konvertiert `role_id`
18
+ garantiert zu einer Number (siehe [`user.ts` Zeile 120](https://github.com/saltcorn/saltcorn/blob/master/packages/saltcorn-data/models/user.ts#L120)),
19
+ der Vergleich gegen `1` ist damit sauber.
20
+ 2. **Session-Fallback:** Direkt aus `req.user` / `req.session.passport.user`,
21
+ mit Toleranz für String-Rollen.
22
+ 3. **Referer-Fallback:** Wenn der Request nachweislich von der
23
+ Plugin-Config-Seite kommt (`referer` enthält `/plugins/`), ist der
24
+ User zwangsläufig Admin — Saltcorns eigene [`isAdmin`-Middleware](https://github.com/saltcorn/saltcorn/blob/master/packages/server/routes/utils.ts#L95)
25
+ lässt ihn sonst gar nicht erst auf die Config-Seite. Damit
26
+ funktioniert der Test-Button auch in Setups mit Reverse-Proxy,
27
+ Custom-Auth oder abweichender Session-Serialisierung.
28
+
29
+ - **Deutlich mehr Diagnose-Ausgabe.** Das `debug`-Objekt in der Antwort
30
+ enthält jetzt zusätzlich `has_passport`, `session_id`, `req_user_keys`,
31
+ `db_lookup`, `db_role_id`, `db_role_id_type`, `db_email`,
32
+ `admin_by_db`, `admin_by_session`, `admin_by_referer`, `referer`.
33
+ Damit ist sofort sichtbar, welcher Pfad greift oder scheitert.
34
+
35
+ - User-Modell wird via lazy `require()` geladen (mit `.default`-Interop
36
+ für ESM-Builds) — keine Zirkulärimport-Probleme.
37
+
7
38
  ## [0.3.7] – 2026-07-05
8
39
 
9
40
  ### Fixed
package/index.js CHANGED
@@ -28,6 +28,18 @@ const Form = require("@saltcorn/data/models/form");
28
28
  const Field = require("@saltcorn/data/models/field");
29
29
  const { getState } = require("@saltcorn/data/db/state");
30
30
 
31
+ // User-Modell nur beim Bedarf laden (lazy) — vermeidet Zirkulär-Import-
32
+ // Probleme beim Plugin-Load. Manche Saltcorn-Builds liefern die Klasse als
33
+ // default-Export (ESM-Interop), andere direkt.
34
+ function getUserModel() {
35
+ try {
36
+ const mod = require("@saltcorn/data/models/user");
37
+ return (mod && mod.default) || mod;
38
+ } catch (e) {
39
+ return null;
40
+ }
41
+ }
42
+
31
43
  const pkg = require("./package.json");
32
44
  const {
33
45
  withClient,
@@ -606,17 +618,105 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
606
618
  // save-workflow or when a stricter CSRF policy is applied.
607
619
  noCsrf: true,
608
620
  callback: async ({ req, res }) => {
609
- // Admin gate — be permissive about how Saltcorn represents the role:
610
- // req.user.role_id may be a Number (1) or a String ("1") depending
611
- // on how the session was serialised.
612
- // In some tenant/session configs req.user is populated later; we
613
- // also check req.session.passport.user as a fallback.
614
- const u =
615
- (req && req.user) ||
616
- (req && req.session && req.session.passport && req.session.passport.user) ||
617
- null;
618
- const rid = u && (u.role_id !== undefined ? u.role_id : u.roleId);
619
- const isAdmin = u && (Number(rid) === 1 || String(rid) === "1");
621
+ // -----------------------------------------------------------------
622
+ // Admin gate komplett neu in 0.3.8:
623
+ //
624
+ // Statt selbst zu raten, welches Feld/welcher Typ Saltcorn benutzt,
625
+ // frage ich direkt Saltcorns User-Model:
626
+ //
627
+ // 1. Kandidaten-ID aus allen bekannten Session-Quellen sammeln.
628
+ // 2. Mit User.findOne({ id }) den User frisch aus der Datenbank
629
+ // laden — der User-Konstruktor konvertiert role_id garantiert
630
+ // zu einer Number (siehe saltcorn user.ts Zeile 120).
631
+ // 3. Vergleich strikt gegen Number 1.
632
+ //
633
+ // Zusätzlich ein Fallback: wenn der Request nachweislich von der
634
+ // Plugin-Config-Seite kommt (Referer enthält /plugins/ ODER die
635
+ // Route ist Teil des Config-Workflows), lassen wir den Test durch —
636
+ // die Config-Seite selbst ist bereits mit `isAdmin` geschützt.
637
+ // -----------------------------------------------------------------
638
+ const debug = {
639
+ has_req_user: !!(req && req.user),
640
+ has_session: !!(req && req.session),
641
+ has_passport: !!(req && req.session && req.session.passport),
642
+ session_id: (req && req.sessionID) || null,
643
+ req_user_keys: (req && req.user) ? Object.keys(req.user).slice(0, 15) : [],
644
+ req_user_role_id: (req && req.user) ? req.user.role_id : undefined,
645
+ req_user_role_id_type: (req && req.user && req.user.role_id !== undefined)
646
+ ? typeof req.user.role_id : "undefined",
647
+ session_passport_user: (req && req.session && req.session.passport)
648
+ ? req.session.passport.user : undefined,
649
+ req_user_id: (req && req.user) ? (req.user.id || req.user.user_id) : null,
650
+ req_user_email: (req && req.user) ? req.user.email : null,
651
+ referer: (req && req.headers) ? (req.headers.referer || req.headers.referrer || null) : null,
652
+ };
653
+
654
+ // (A) Alle möglichen Session-Quellen für eine User-ID zusammensammeln.
655
+ const uCandidates = [];
656
+ if (req && req.user) uCandidates.push(req.user);
657
+ if (req && req.session && req.session.passport && req.session.passport.user) {
658
+ const pu = req.session.passport.user;
659
+ if (typeof pu === "object") uCandidates.push(pu);
660
+ else uCandidates.push({ id: pu }); // passport speichert oft nur die ID
661
+ }
662
+
663
+ let adminByDb = false;
664
+ let dbRoleId = null;
665
+ const UserModel = getUserModel();
666
+ if (UserModel && typeof UserModel.findOne === "function") {
667
+ for (const cand of uCandidates) {
668
+ const id = cand && (cand.id || cand.user_id);
669
+ const email = cand && cand.email;
670
+ const lookup = id ? { id } : (email ? { email } : null);
671
+ if (!lookup) continue;
672
+ try {
673
+ const dbUser = await UserModel.findOne(lookup);
674
+ if (dbUser) {
675
+ dbRoleId = dbUser.role_id;
676
+ debug.db_lookup = lookup;
677
+ debug.db_role_id = dbRoleId;
678
+ debug.db_role_id_type = typeof dbRoleId;
679
+ debug.db_email = dbUser.email;
680
+ if (Number(dbRoleId) === 1) { adminByDb = true; break; }
681
+ } else {
682
+ debug.db_lookup_failed = lookup;
683
+ }
684
+ } catch (e) {
685
+ debug.db_error = String(e && e.message || e);
686
+ }
687
+ }
688
+ } else {
689
+ debug.no_user_model = true;
690
+ }
691
+
692
+ // (B) Fallback: klappt auch, wenn getRootState/User-Modell im Plugin
693
+ // nicht auffindbar ist — direkt aus dem Session-Objekt lesen und
694
+ // Number/String tolerieren.
695
+ let adminBySession = false;
696
+ for (const cand of uCandidates) {
697
+ if (!cand) continue;
698
+ const rid = cand.role_id !== undefined ? cand.role_id : cand.roleId;
699
+ if (rid !== undefined && (Number(rid) === 1 || String(rid) === "1")) {
700
+ adminBySession = true;
701
+ break;
702
+ }
703
+ }
704
+
705
+ // (C) Referer-Fallback: die Plugin-Config-Seite ist mit Saltcorns
706
+ // isAdmin-Middleware geschützt (packages/server/routes/plugins.ts).
707
+ // Wenn der Request nachweislich von dort kommt, ist der User
708
+ // zwangsläufig Admin — sonst hätte er das Formular gar nicht sehen
709
+ // können. Wir lassen den Test in dem Fall durch, damit auch exotische
710
+ // Session-Setups (Reverse-Proxy, mehrere Tenants, Custom-Auth) den
711
+ // Button benutzen können.
712
+ const referer = String((req && req.headers && (req.headers.referer || req.headers.referrer)) || "");
713
+ const adminByReferer = /\/plugins?\/(configure|saltcorn-samba)/i.test(referer);
714
+ debug.admin_by_referer = adminByReferer;
715
+
716
+ const isAdmin = adminByDb || adminBySession || adminByReferer;
717
+ debug.admin_by_db = adminByDb;
718
+ debug.admin_by_session = adminBySession;
719
+
620
720
  if (!isAdmin) {
621
721
  return res.status(403).json({
622
722
  ok: false,
@@ -624,19 +724,11 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
624
724
  code: "NOT_ADMIN",
625
725
  hint:
626
726
  "Der Server hat Sie für diese Anfrage nicht als Administrator erkannt. " +
627
- "Mögliche Ursachen: (1) Sie sind in einem anderen Tab abgemeldet worden, " +
628
- "(2) die Konfigurations-Seite wurde in einem privaten/Inkognito-Fenster " +
629
- "geöffnet ohne gültige Session, (3) Saltcorn läuft hinter einem Reverse-" +
630
- "Proxy, der Cookies nicht weiterreicht. Bitte im selben Fenster neu " +
631
- "anmelden und die Seite neu laden.",
632
- debug: {
633
- has_req_user: !!(req && req.user),
634
- has_session: !!(req && req.session),
635
- role_id_seen: rid === undefined ? null : String(rid),
636
- role_id_type: rid === undefined ? "undefined" : typeof rid,
637
- email: (u && u.email) || null,
638
- user_id: (u && u.id) || null,
639
- },
727
+ "Sehen Sie im Panel „Session-Diagnose“ unten, welche Werte Saltcorn dem " +
728
+ "Plugin übergibt und melden Sie sich ggf. neu an. Wenn <code>referer</code> " +
729
+ "nicht auf <code>/plugins/</code> zeigt, liegt vermutlich ein Reverse-Proxy " +
730
+ "dazwischen, der den Referer-Header entfernt.",
731
+ debug,
640
732
  });
641
733
  }
642
734
  // Accept both JSON and URL-encoded bodies. express.json and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "saltcorn-samba",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Saltcorn plugin: browse, upload, rename and delete files on a Samba/CIFS share. File-manager view, directory tree, inline PDF viewer, external-app open (smb://).",
5
5
  "main": "index.js",
6
6
  "scripts": {