fix(api): tenant-проверка account/template в CreateDomain (HIGH), атомарный import через транзакцию (MEDIUM)

This commit is contained in:
2026-07-03 15:08:16 +07:00
parent ae6a4d7f4c
commit 2aca92d070
5 changed files with 288 additions and 11 deletions
+25 -8
View File
@@ -127,14 +127,16 @@ func (a *API) handleImportZones(w http.ResponseWriter, r *http.Request) {
writeErr(w, http.StatusInternalServerError, "internal error")
return
}
created := make([]domainResponse, 0, len(zones))
for _, z := range zones {
d, err := a.Store.CreateDomain(r.Context(), pid, aid, z.Name, z.ID, nil)
if err != nil {
log.Printf("api: import: create domain failed: %v", err)
writeErr(w, http.StatusInternalServerError, "internal error")
return
}
// Imported atomically: either every zone becomes a domain or none does,
// so a mid-batch provider/DB error never leaves a partial import behind.
doms, err := a.Store.ImportDomains(r.Context(), pid, aid, zones)
if err != nil {
log.Printf("api: import: create domains failed: %v", err)
writeErr(w, http.StatusInternalServerError, "internal error")
return
}
created := make([]domainResponse, 0, len(doms))
for _, d := range doms {
created = append(created, toDomainResponse(d))
}
writeJSON(w, http.StatusCreated, created)
@@ -259,6 +261,21 @@ func (a *API) handleCreateDomain(w http.ResponseWriter, r *http.Request) {
writeErr(w, http.StatusBadRequest, "invalid templateId")
return
}
// Tenant isolation: the account (and template, if given) must belong to
// this project — otherwise a caller could attach a domain to another
// tenant's provider account or DNS template.
if _, err := a.Store.GetAccount(r.Context(), accID, pid); err != nil {
writeErr(w, http.StatusNotFound, "provider account not found")
return
}
if templateID != nil {
if _, err := a.Store.GetTemplate(r.Context(), *templateID, pid); err != nil {
writeErr(w, http.StatusNotFound, "template not found")
return
}
}
dom, err := a.Store.CreateDomain(r.Context(), pid, accID, req.ZoneName, req.ZoneID, templateID)
if err != nil {
log.Printf("api: create domain failed: %v", err)