-- ───────────────────────────────────────────────────────────────────────────── -- ic-web – Verwaltung -- -- Zwei Ebenen, beide ueber die Anmeldung: -- -- superadmin legt Domaenen an, setzt je Domaene einen Admin ein, sperrt -- und loescht. Sieht alles. -- admin verwaltet die Zugaenge seiner Domaene und pflegt ihre Seiten. -- -- Ein Zugang ist zugleich ein Postfach: wer hier einen Zugang anlegt, legt -- damit die Mailadresse mit an. -- ───────────────────────────────────────────────────────────────────────────── ICWeb = ICWeb or {} ICWeb.Manage = {} local M = ICWeb.Manage local DB = ICWeb.DB local FW = ICWeb.FW local D = ICWeb.Domains local A = ICWeb.Auth -- Was der Client ueber einen Zugang erfahren darf. local function publicAccount(row) return { id = row.id, username = row.username, domain = row.domain, role = row.role, display_name = row.display_name, active = ICWeb.Bool(row.active), must_change = ICWeb.Bool(row.must_change), last_login = row.last_login, created_at = row.created_at, } end -- ── Zugaenge einer Domaene ────────────────────────────────────────────────── function M.ListAccounts(src, domain) domain = tostring(domain or ''):lower() if not A.CanManageAccounts(src, domain) then return nil, 'Keine Berechtigung.' end local out = {} for _, row in ipairs(DB.GetAccountsForDomain(domain)) do out[#out + 1] = publicAccount(row) end return out, nil end -- Gibt (zugang, nil) oder (nil, fehlertext) zurueck. -- -- Das Passwort wird vom Anleger vergeben und danach weitergegeben. Es kommt -- deshalb einmal im Klartext zurueck an den Bildschirm des Anlegers, aber nie -- wieder aus der Datenbank heraus. function M.CreateAccount(src, domain, username, password, role, displayName) domain = tostring(domain or ''):lower() if not A.CanManageAccounts(src, domain) then return nil, 'Keine Berechtigung.' end local site = DB.GetSiteByDomain(domain) if not site then return nil, 'Domäne nicht gefunden.' end -- Nur der Anbieter darf weitere Anbieterzugaenge oder Domaenenadmins -- ausserhalb der eigenen Domaene anlegen. if role == 'superadmin' and not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung für diese Rolle.' end if role ~= 'admin' and role ~= 'editor' and role ~= 'superadmin' then return nil, 'Unbekannte Rolle.' end if DB.CountAccountsForDomain(domain) >= (Config.MaxAccountsPerDomain or 15) then return nil, ('Höchstens %d Zugänge je Domäne.'):format(Config.MaxAccountsPerDomain or 15) end local session = A.Get(src) local id, err = A.CreateAccount({ username = username, domain = domain, role = role, display_name = displayName, password = password, must_change = true, created_by = session and session.username or 'system', }) if not id then return nil, err end ICWeb.Log.Write(src, 'account.created', { domain = domain, username = tostring(username):lower(), role = role }) return publicAccount(DB.GetAccountById(id)), nil end function M.SetAccountPassword(src, accountId, password) local account = DB.GetAccountById(tonumber(accountId) or -1) if not account then return false, 'Zugang nicht gefunden.' end -- Den eigenen Zugang darf jeder aendern, fremde nur die Verwaltung. local session = A.Get(src) local own = session ~= nil and session.username == account.username if not own and not A.CanManageAccounts(src, account.domain) then return false, 'Keine Berechtigung.' end -- Der Anbieterzugang laesst sich nur von ihm selbst oder einem anderen -- Anbieterzugang aendern. if account.role == 'superadmin' and not own and not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end local ok, err = A.SetPassword(account.id, password) if not ok then return false, err end if own then A.ClearMustChange(src) end ICWeb.Log.Write(src, 'account.password.changed', { username = account.username }) return true, nil end function M.SetAccountActive(src, accountId, active) local account = DB.GetAccountById(tonumber(accountId) or -1) if not account then return false, 'Zugang nicht gefunden.' end if not A.CanManageAccounts(src, account.domain) then return false, 'Keine Berechtigung.' end if account.role == 'superadmin' and not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end local session = A.Get(src) if session and session.username == account.username then return false, 'Den eigenen Zugang kannst du nicht sperren.' end DB.SetAccountActive(account.id, active == true) ICWeb.Log.Write(src, active and 'account.enabled' or 'account.disabled', { username = account.username }) return true, nil end function M.SetAccountRole(src, accountId, role) local account = DB.GetAccountById(tonumber(accountId) or -1) if not account then return false, 'Zugang nicht gefunden.' end if not A.CanManageAccounts(src, account.domain) then return false, 'Keine Berechtigung.' end if role ~= 'admin' and role ~= 'editor' then return false, 'Unbekannte Rolle.' end if account.role == 'superadmin' then return false, 'Der Anbieterzugang bleibt, wie er ist.' end local session = A.Get(src) if session and session.username == account.username then return false, 'Die eigene Rolle kannst du nicht ändern.' end DB.SetAccountRole(account.id, role) ICWeb.Log.Write(src, 'account.role.changed', { username = account.username, role = role }) return true, nil end -- Loeschen entfernt den Zugang, nicht das Postfach: dort liegen Nachrichten. function M.DeleteAccount(src, accountId) local account = DB.GetAccountById(tonumber(accountId) or -1) if not account then return false, 'Zugang nicht gefunden.' end if not A.CanManageAccounts(src, account.domain) then return false, 'Keine Berechtigung.' end if account.role == 'superadmin' then return false, 'Der Anbieterzugang lässt sich nicht löschen.' end local session = A.Get(src) if session and session.username == account.username then return false, 'Den eigenen Zugang kannst du nicht löschen.' end DB.DeleteAccount(account.id) ICWeb.Log.Write(src, 'account.deleted', { username = account.username }, 'warn') return true, nil end -- ── Anbieterebene ─────────────────────────────────────────────────────────── function M.AdminListSites(src) if not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung.' end local out = {} for _, s in ipairs(DB.GetAllSites()) do out[#out + 1] = { domain = s.domain, title = s.title, owner_type = s.owner_type, owner_id = s.owner_id, published = ICWeb.Bool(s.published), blocked = ICWeb.Bool(s.blocked), pages = DB.CountPages(s.id), accounts = DB.CountAccountsForDomain(s.domain), created_at = s.created_at, } end return out, nil end -- Domaene anlegen und gleich den ersten Zugang dafuer einrichten. Ohne diesen -- Zugang koennte niemand die neue Seite pflegen. -- -- Gibt (site, nil, zugangsdaten) oder (nil, fehlertext) zurueck. function M.AdminRegister(src, domain, title, ownerId, adminUser, adminPassword) if not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung.' end -- Der Anbieter darf auch Behoerdennamen vergeben – dafuer ist er da. local name, err = D.CheckName(domain, true) if not name then return nil, err end ownerId = tostring(ownerId or ''):lower() if ownerId == '' then return nil, 'Kein Besitzer (Job) angegeben.' end if DB.GetSiteByDomain(name) then return nil, 'Diese Domäne ist bereits vergeben.' end -- Zugangsdaten zuerst pruefen: eine Domaene ohne Zugang waere unbrauchbar, -- und ein Rueckbau nach dem Anlegen ist fehleranfaelliger als eine -- Pruefung davor. local user = tostring(adminUser or 'admin'):lower():gsub('[^a-z0-9%.%-_]', '') if user == '' then user = 'admin' end local username = user .. '@' .. name local password = adminPassword local generated = false if type(password) ~= 'string' or password == '' then password = D.RandomPassword() generated = true end local pwErr = A.CheckPassword(password) if pwErr then return nil, pwErr end local realmOk, realmErr = D.EnsureRealm(name, title, ownerId) if not realmOk then return nil, realmErr end local site = D.CreateSiteWithHome(name, title, 'job', ownerId, (A.Get(src) or {}).username or 'system') if not site then return nil, 'Speichern fehlgeschlagen.' end local accountId, accErr = A.CreateAccount({ username = username, domain = name, role = 'admin', display_name = title or name, password = password, must_change = true, created_by = (A.Get(src) or {}).username or 'system', }) if not accountId then -- Ohne Zugang ist die Domaene nicht pflegbar; lieber nichts stehen lassen. DB.DeleteSite(site.id) return nil, accErr end ICWeb.Log.Write(src, 'domain.registered', { domain = name, owner = ownerId }, 'warn') return site, nil, { username = username, password = password, generated = generated } end function M.AdminSetBlocked(src, domain, blocked) if not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end local site = DB.GetSiteByDomain(tostring(domain or ''):lower()) if not site then return false, 'Domäne nicht gefunden.' end DB.SetBlocked(site.id, blocked == true) ICWeb.Log.Write(src, blocked and 'site.blocked' or 'site.unblocked', { domain = site.domain }, 'warn') return true, nil end -- Besitzer wechseln, etwa wenn eine Firma verkauft wird. function M.AdminSetOwner(src, domain, ownerId) if not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end local site = DB.GetSiteByDomain(tostring(domain or ''):lower()) if not site then return false, 'Domäne nicht gefunden.' end ownerId = tostring(ownerId or ''):lower() if ownerId == '' then return false, 'Kein Besitzer angegeben.' end DB.SetOwner(site.id, 'job', ownerId) ICWeb.Log.Write(src, 'site.owner.changed', { domain = site.domain, from = site.owner_id, to = ownerId }, 'warn') return true, nil end -- Loeschen entfernt Seiten und Meldungen ueber ON DELETE CASCADE sowie die -- Zugaenge dieser Domaene. Die Mail-Domaene und die Postfaecher bleiben -- bestehen: dort liegen Nachrichten, die eine geloeschte Webseite nichts angehen. function M.AdminDeleteSite(src, domain) if not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end local site = DB.GetSiteByDomain(tostring(domain or ''):lower()) if not site then return false, 'Domäne nicht gefunden.' end for _, acc in ipairs(DB.GetAccountsForDomain(site.domain)) do DB.DeleteAccount(acc.id) end DB.DeleteSite(site.id) ICWeb.Log.Write(src, 'site.deleted', { domain = site.domain }, 'warn') return true, nil end function M.AdminListReports(src) if not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung.' end return DB.GetOpenReports(), nil end function M.AdminSetReportStatus(src, id, status) if not A.IsSuperadmin(src) then return false, 'Keine Berechtigung.' end if status ~= 'reviewed' and status ~= 'dismissed' and status ~= 'open' then return false, 'Unbekannter Status.' end DB.SetReportStatus(tonumber(id) or -1, status) return true, nil end -- Jobliste fuer das Besitzerfeld in der Verwaltung. function M.AdminListJobs(src) if not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung.' end return FW.GetJobs(), nil end -- Alle Zugaenge quer ueber alle Domaenen. function M.AdminListAccounts(src) if not A.IsSuperadmin(src) then return nil, 'Keine Berechtigung.' end local out = {} for _, row in ipairs(DB.GetAllAccounts()) do out[#out + 1] = publicAccount(row) end return out, nil end