-- ───────────────────────────────────────────────────────────────────────────── -- ic-web – Domaenen: Registrierung, Besitz, Bearbeitungsrecht -- -- Domaenen vergibt der Anbieter ueber die Verwaltung (ICWeb.Manage). Die -- Selbstanmeldung ueber den ESX-Job weiter unten ist der aeltere Weg und -- standardmaessig ausgeschaltet – siehe Config.SelfRegistration. -- -- Die Domaene selbst wird in ic-mail angelegt. Dadurch bekommt eine Firma mit -- der Webseite automatisch auch die passende Mail-Domaene, und es kann nicht -- passieren, dass zwei Systeme dieselbe Domaene verschiedenen Besitzern geben. -- ───────────────────────────────────────────────────────────────────────────── ICWeb = ICWeb or {} ICWeb.Domains = {} local D = ICWeb.Domains local DB = ICWeb.DB local FW = ICWeb.FW -- ── Namenspruefung ────────────────────────────────────────────────────────── -- Gibt (domain, nil) oder (nil, fehlertext) zurueck. -- -- allowReserved hebt nur die Namensliste auf, nicht die Formregeln. Sie soll -- verhindern, dass Spieler sich Behoerdennamen greifen; der Anbieter vergibt -- diese Namen ja gerade absichtlich. Der technische Schutz – Domaenen des -- Mailsystems – haengt nicht hier, sondern in D.EnsureRealm und gilt fuer alle. function D.CheckName(raw, allowReserved) if type(raw) ~= 'string' then return nil, 'kein Name angegeben' end local domain = raw:lower():gsub('%s', '') local name, tld = domain:match('^([a-z0-9%-]+)%.([a-z]+)$') if not name then return nil, 'Format: name.endung, nur Buchstaben, Ziffern und Bindestrich' end if #name < Config.Domain.minLength or #name > Config.Domain.maxLength then return nil, ('Name muss %d bis %d Zeichen lang sein') :format(Config.Domain.minLength, Config.Domain.maxLength) end if name:sub(1, 1) == '-' or name:sub(-1) == '-' then return nil, 'Name darf nicht mit Bindestrich beginnen oder enden' end local tldOk = false for _, allowed in ipairs(Config.Domain.allowedTlds) do if tld == allowed then tldOk = true break end end if not tldOk then return nil, ('Endung nicht erlaubt. Moeglich: %s') :format(table.concat(Config.Domain.allowedTlds, ', ')) end if not allowReserved then for _, reserved in ipairs(Config.Domain.reserved) do if name == reserved then return nil, 'dieser Name ist reserviert' end end end return domain, nil end -- ── Darf dieser Spieler ohne Anmeldung fuer seinen Job registrieren? ───────── -- Gibt (ownerType, ownerId, nil) oder (nil, nil, fehlertext) zurueck. -- -- Der Normalfall ist ausgeschaltet: Domaenen vergibt der Anbieter ueber die -- Verwaltung. Siehe Config.SelfRegistration. function D.CanRegister(src) if not Config.SelfRegistration.enabled then return nil, nil, 'Domaenen vergibt die Administration von Liveinvader' end local job, grade = FW.GetJob(src) if not job then return nil, nil, 'kein Job' end if Config.Registration.blockedJobs[job] then return nil, nil, 'mit diesem Job nicht moeglich' end local allowed = Config.Registration.allowedJobs if allowed and next(allowed) ~= nil then local found = false for _, j in ipairs(allowed) do if j == job then found = true break end end if not found then return nil, nil, 'dieser Job ist nicht freigeschaltet' end end if grade < (Config.Registration.minGrade or 0) then return nil, nil, ('Dienstgrad zu niedrig (mindestens %d)') :format(Config.Registration.minGrade or 0) end local count = DB.CountSitesForOwner('job', job) if count >= (Config.Registration.maxDomainsPerJob or 1) then return nil, nil, ('Grenze erreicht: %d Domaenen je Job'):format(count) end return 'job', job, nil end -- ── Bearbeitungsrecht ─────────────────────────────────────────────────────── -- Ausschliesslich ueber die Anmeldung: wer mit einem Zugang dieser Domaene -- angemeldet ist, darf ihre Seiten pflegen. Job und Serverrechte spielen keine -- Rolle mehr – wer den Zugang kennt, verwaltet die Seite, unabhaengig davon, -- welchen Charakter er gerade spielt. function D.CanEdit(src, site) if not site then return false end return ICWeb.Auth.CanEdit(src, site.domain) end -- ── Mail-Domaene sicherstellen ────────────────────────────────────────────── -- Gibt (true, nil) oder (false, fehlertext) zurueck. -- -- Laeuft ic-mail nicht, gilt das als Erfolg: die Webseite funktioniert -- eigenstaendig, es gibt dann nur keine Mailadressen darauf. function D.EnsureRealm(domain, title, ownerId) if GetResourceState('ic-mail') ~= 'started' then return true, nil end local existing = exports['ic-mail']:GetRealmByName(domain) if existing then -- System- und Buergerdomaenen gehoeren niemandem und haben deshalb -- owner_id = NULL. Ohne diese Pruefung wuerde die Besitzpruefung unten -- durchwinken, und eine Firmenwebseite haenge plötzlich an mail.ls – -- also an der Domaene saemtlicher persoenlicher Postfaecher. if ICWeb.Bool(existing.is_default) or existing.type == 'system' or existing.type == 'citizen' then return false, 'diese Domaene gehoert zum Mailsystem und ist nicht vergebbar' end -- Domaene gibt es schon fuer Mail. Nur uebernehmen, wenn sie demselben -- Besitzer gehoert – sonst wuerde die Webseite die Mailadressen einer -- fremden Firma kapern. if existing.owner_id and existing.owner_id ~= ownerId then return false, 'diese Domaene gehoert bereits einer anderen Stelle' end return true, nil end local ok, createErr = exports['ic-mail']:CreateRealm({ realm = domain, type = 'company', display_name = title or domain, owner_id = ownerId, }) if not ok then return false, ('Domaene konnte nicht angelegt werden: %s'):format(tostring(createErr)) end return true, nil end -- ── Registrieren ──────────────────────────────────────────────────────────── -- Gibt (site, nil) oder (nil, fehlertext) zurueck. function D.Register(src, rawDomain, title) local ownerType, ownerId, err = D.CanRegister(src) if not ownerType then return nil, err end local domain, nameErr = D.CheckName(rawDomain) if not domain then return nil, nameErr end if DB.GetSiteByDomain(domain) then return nil, 'diese Domaene ist bereits vergeben' end local realmOk, realmErr = D.EnsureRealm(domain, title, ownerId) if not realmOk then return nil, realmErr end local site = D.CreateSiteWithHome(domain, title, ownerType, ownerId, FW.GetIdentifier(src)) if not site then return nil, 'Speichern fehlgeschlagen' end -- Bei der Selbstanmeldung gibt es niemanden, der einen Zugang einrichten -- koennte. Also wird er hier erzeugt und einmalig angezeigt. local username = 'admin@' .. domain local password = D.RandomPassword() local accountId = ICWeb.Auth.CreateAccount({ username = username, domain = domain, role = 'admin', display_name = title or domain, password = password, must_change = true, created_by = 'selfservice', }) ICWeb.Log.Write(src, 'domain.registered', { domain = domain, owner = ownerId }) return site, nil, accountId and { username = username, password = password } or nil end -- Domaene, Startseite und Rueckgabe in einem. Beide Anlagewege gehen hier durch, -- damit eine neue Domaene nie ohne Startseite dasteht. function D.CreateSiteWithHome(domain, title, ownerType, ownerId, createdBy) local siteId = DB.CreateSite({ domain = domain, title = tostring(title or domain):sub(1, 120), theme = Config.DefaultTheme, owner_type = ownerType or 'job', owner_id = ownerId, published = false, created_by = createdBy, }) if not siteId then return nil end DB.SavePage(siteId, 'home', title or domain, json.encode({ { type = 'heading', text = title or domain, level = 1 }, { type = 'text', text = 'Diese Seite wurde noch nicht bearbeitet.' }, }), 0) return DB.GetSiteById(siteId) end -- Aussprechbares Startpasswort. Es muss beim ersten Anmelden geaendert werden, -- deshalb zaehlt Merkbarkeit hier mehr als Laenge. function D.RandomPassword() local words = { 'anker', 'birke', 'delta', 'falke', 'gipfel', 'hafen', 'insel', 'kompass', 'lotse', 'nordwind', 'orkan', 'rebe' } return words[math.random(#words)] .. tostring(math.random(100, 999)) end