221 lines
9.3 KiB
Lua
221 lines
9.3 KiB
Lua
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
-- 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
|