Changeset 98ecf51 in Klonkt
- Timestamp:
- 06/18/2026 03:19:09 AM (3 months ago)
- Branches:
- main
- Children:
- 7881080
- Parents:
- 6a2abb1
- Location:
- src
- Files:
-
- 4 edited
-
middleware/auth.js (modified) (3 diffs)
-
routes/admin-sites.js (modified) (7 diffs)
-
services/PermissionsService.js (modified) (2 diffs)
-
views/pages/admin-site-edit.ejs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/middleware/auth.js
r6a2abb1 r98ecf51 4 4 5 5 import db from '../config/database.js'; 6 import PermissionsService from '../services/PermissionsService.js'; 6 7 7 8 /** … … 65 66 if (u.role === 'god' || u.role === 'kijker') return next(); // kijker = alleen-lezen kijk-toegang 66 67 const site = res.locals.site; 67 if (site && site.owner_id === u.id) return next(); 68 // owner OF toegewezen mede-beheerder (site_members) — canAdminSite dekt beide. 69 if (site && PermissionsService.canAdminSite(u, site)) return next(); 68 70 return res.status(403).send('Geen toegang tot deze site.'); 69 71 } … … 74 76 const u = req.session.user; 75 77 if (u.role === 'god' || u.role === 'kijker') return next(); // kijker = alleen-lezen kijk-toegang 76 const site = db.prepare('SELECT owner_id FROM sites WHERE slug = ?').get(req.params.slug);77 if (site && site.owner_id === u.id) return next();78 const site = db.prepare('SELECT id, owner_id FROM sites WHERE slug = ?').get(req.params.slug); 79 if (site && PermissionsService.canAdminSite(u, site)) return next(); 78 80 return res.status(403).send('Geen toegang tot deze site.'); 79 81 } -
src/routes/admin-sites.js
r6a2abb1 r98ecf51 137 137 } 138 138 139 /** Geldige user-id voor owner-toewijzing, of null bij leeg/onbekend. */ 140 function validOwnerId(raw) { 141 const id = (raw || '').toString().trim(); 142 if (!id) return null; 143 return db.prepare('SELECT 1 FROM users WHERE id = ?').get(id) ? id : null; 144 } 145 146 /** Geef een user admin-rechten op een site (idempotent upsert). */ 147 function grantSiteAdmin(siteId, userId) { 148 db.prepare(` 149 INSERT INTO site_members (site_id, user_id, role) VALUES (?, ?, 'admin') 150 ON CONFLICT(site_id, user_id) DO UPDATE SET role = 'admin' 151 `).run(siteId, userId); 152 } 153 154 /** Kandidaat-owners voor het owner-keuzeveld (god-only). */ 155 function listOwnerCandidates() { 156 return db.prepare('SELECT id, username, role FROM users ORDER BY username').all(); 157 } 158 139 159 // ==================== LIST ==================== 140 160 router.get('/', requireGod, (req, res) => { … … 163 183 bodyClass: 'on-admin', 164 184 isNew: true, 165 site: { slug: '', ...siteEditableFields() }, 185 site: { slug: '', owner_id: req.session.user.id, ...siteEditableFields() }, 186 users: listOwnerCandidates(), 166 187 palettes: ThemeService.listPalettes(), 167 188 accents: ThemeService.listAccents(), … … 187 208 188 209 const f = { ...siteEditableFields(), ...req.body }; 210 211 // Owner: god mag de site aan een ANDERE gebruiker toewijzen — dit is de kern 212 // van hub-modus (elke gebruiker z'n eigen, zelf te beheren Klonkt). Leeg of 213 // ongeldig → de aanmakende god zelf. 214 const ownerId = validOwnerId(req.body.owner_id) || req.session.user.id; 215 189 216 const siteId = uuid(); 190 217 db.prepare(` … … 200 227 (f.description || '').slice(0, 500), 201 228 (f.tagline || '').slice(0, 200), 202 req.session.user.id,229 ownerId, 203 230 f.language || 'nl', 204 231 f.palette || 'sage', … … 213 240 ); 214 241 215 // The site_members entry lets the god/owner show up in canAdminSite checks. 216 db.prepare(` 217 INSERT INTO site_members (site_id, user_id, role) VALUES (?, ?, 'admin') 218 `).run(siteId, req.session.user.id); 219 220 res.redirect(`/admin/sites/${slug}/edit?success=` + encodeURIComponent('Site created')); 242 // De OWNER (niet per se de aanmaker) krijgt een site_members-admin-rij → zo komt 243 // 'ie door canAdminSite + de requireSiteManager-gates en beheert 'ie z'n site. 244 grantSiteAdmin(siteId, ownerId); 245 246 res.redirect(`/admin/sites/${slug}/edit?success=` + encodeURIComponent('Site aangemaakt')); 221 247 }); 222 248 … … 236 262 isNew: false, 237 263 site, 264 users: listOwnerCandidates(), 238 265 palettes: ThemeService.listPalettes(), 239 266 accents: ThemeService.listAccents(), … … 314 341 ); 315 342 316 res.redirect(`/admin/sites/${req.params.slug}/edit?success=` + encodeURIComponent('Saved')); 343 // Owner (her)toewijzen — ALLEEN god. Een site-owner die z'n eigen site bewerkt 344 // kan de eigenaar niet wijzigen (het veld wordt voor niet-god ook niet getoond). 345 if (req.session.user.role === 'god') { 346 const newOwner = validOwnerId(req.body.owner_id); 347 if (newOwner) { 348 db.prepare('UPDATE sites SET owner_id = ? WHERE id = ?').run(newOwner, site.id); 349 grantSiteAdmin(site.id, newOwner); 350 } 351 } 352 353 res.redirect(`/admin/sites/${req.params.slug}/edit?success=` + encodeURIComponent('Opgeslagen')); 317 354 }); 318 355 -
src/services/PermissionsService.js
r6a2abb1 r98ecf51 3 3 * Used in templates to show/hide edit buttons, delete buttons, etc. 4 4 */ 5 6 import db from '../config/database.js'; 5 7 6 8 class PermissionsService { … … 54 56 */ 55 57 static canAdminSite(user, site) { 56 if (!user ) return false;58 if (!user || !site) return false; 57 59 if (user.role === 'god') return true; 58 60 if (user.id === site.owner_id) return true; 59 // Check site_members table 60 return user.siteRoles && user.siteRoles[site.id] === 'admin'; 61 // Toegewezen mede-beheerder (collaborator) via site_members. Dit werd 62 // voorheen via een nooit-gevulde user.siteRoles gelezen → dode code; nu 63 // direct op de tabel (paar checks per pagina, indexed = goedkoop). 64 return !!db.prepare( 65 "SELECT 1 FROM site_members WHERE site_id = ? AND user_id = ? AND role = 'admin' LIMIT 1" 66 ).get(site.id, user.id); 61 67 } 62 68 -
src/views/pages/admin-site-edit.ejs
r6a2abb1 r98ecf51 24 24 <input type="text" name="title" value="<%= site.title || '' %>" required maxlength="200"> 25 25 </label> 26 <%# Eigenaar (her)toewijzen — ALLEEN god. In hub-modus geef je zo elke 27 gebruiker z'n eigen, zelf te beheren Klonkt. Niet-god ziet dit niet. %> 28 <% if (user && user.role === 'god') { %> 29 <label> 30 <span>Eigenaar <small class="form-hint-inline">— wie deze Klonkt zelf mag beheren</small></span> 31 <select name="owner_id"> 32 <% (typeof users !== 'undefined' ? users : []).forEach(function(u){ %> 33 <option value="<%= u.id %>" <%= (site.owner_id === u.id) ? 'selected' : '' %>><%= u.username %><%= u.role === 'god' ? ' (god)' : '' %></option> 34 <% }); %> 35 </select> 36 </label> 37 <% } %> 26 38 <label> 27 39 <span>Tagline <small class="form-hint-inline">— korte oneliner</small></span>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)