mirror of
https://github.com/gravitl/netmaker.git
synced 2026-10-12 02:15:06 +00:00
* wip(go): return auth token with user on create org owner; * wip(go): prevent use of default tenant slug ; * wip(go): upsert settings for tenant in ctx; * wip(go): use passed ctx when updating user; remove password validation from ValidateUser (already validated in UpdateUser); * wip(go): qualify all tenant id usages with table name; * wip(go): use passed ctx * wip(go): return old enrollment key type on create; * wip(go): prevent null networks return; * wip(go): wire ctx in dns/nameserver functions; wire ctx in host registration functions; * wip(go): wire ctx in node functions; * wip(go): wire ctx in node functions; * wip(go): wire ctx in extclient functions; * wip(go): remove not needed field; * wip(go): use passed ctx; * wip(go): use unique index instead of primary key; * wip(go): wire ctx in extclient apis; * wip(go): wire ctx in extclient apis; * wip(go): remove org id check from global scoped get tenant api; * wip(go): add scope slug to discover response; * wip(go): wire ctx in get users; * wip(go): set tenant id in posture check violations; * wip(go): upsert membership instead of user; * wip(go): pass correct len; * wip(go): optimize (single loop); * wip(go): unify todos; * wip(go): add todo; * wip(go): wire context for network get; * wip(go): wire context; * wip(go): wire context; * wip(go): wire ctx when getting tenant superadmin; * wip(go): add network creator to network admin group; * wip(go): check for user membership on sync; * wip(go): add methods with strict membership check; * wip(go): remove unwanted feature flags; * wip(go): report per-tenant usage during license validation; * wip(go): move email setting from tenant to org; * wip(go): pass org scope to email init; * wip(go): remove old tenant email settings usages; use org settings to get email settings * wip(go): use get email settings from org settings; * wip(go): remove email settings migration; * wip(go): migrate email settings from server to org settings; * wip(go): assign global network admin group membership only for super-admin and admins; * wip(go): assign global network admin group membership only for super-admin and admins; * wip(go): add membership alias to filter; * wip(go): skip clearing local license validation response cache; * wip(go): extend license validation response for multi-tenancy; * wip(go): add functions to get sole tenant and org; * wip(go): use tenant-specific feature flags; * wip(go): add tenant status type; * wip(go): remove old hook comment; * wip(go): expect ctx during license validation; * wip(go): always create default org and tenant for ce; * wip(go): add tenant-specific check for license validation error; * wip(go): cache license validation response in memory; * wip(go): add todo to handle multiple-tenant for integration pull; * wip(go): pass ctx during license validation; * wip(go): add todo for making changes later; run hook separately for each tenant; * wip(go): return specific errors when license validation response not in cache; * wip(go): use retryable.httpclient for calling license validation api; * wip(go): process license validation; * wip(go): license validation function; * wip(go): migration on license validation; * wip(go): org settings not expected; * wip(go): remove expiry from license validation response; * wip(go): add limits and limits enforcement to license validation response; * wip(go): remove expiry usages; * wip(go): add arg to clear cache; * wip(go): add arg to clear cache; * wip(go): skip license validation if cached resp; * wip(go): allow org_id and tenant_id query params; * wip(go): upsert tenant membership on sync; * wip(go): pass ctx to delete network roles; * wip(go): create tenant membership on org owner creation on ce; * wip(go): add tenant orchestrator; * wip(go): use tenant orchestrator; * wip(go): add default settings for tenant; * wip(go): make org owner superadmin on all tenants; * wip(go): simplify SyncOrgAndTenants; * wip(go): retry license validation only 3 times * wip(go): store invalid err; * wip(go): add function to check if limits are to be enforced on a tenant; * wip(go): pass tenant scope if possible when checking for license validation error; * wip(go): remove merge errors; * wip(go): add functions for limit checks; * wip(go): wire host limit check; * wip(go): wire ingress limit check; * wip(go): wire egress limit check; * wip(go): wire client limit check; * wip(go): wire network limit check; * wip(go): wire user limit check; * wip(go): use correct errors; * wip(go): skip telemetry for msps; * wip(go): disable telemetry on msp tenants; * wip(go): err check; * wip(go): uncomment log event; * wip(go): get sso state to get scope for non-headless oauth login; * wip(go): inline delete pending user; * wip(go): allow both org and tenant scope for pending users table; * wip(go): add migration for pending user table during set tenant id and rekey org/tenant id; * wip(go): update queries to use new scope fields in pending users table; * wip(go): allow both org and tenant scope for user invites table; * wip(go): update queries to use new scope fields; * wip(go): add migration for rekey-ing org/tenant id and setting tenant id; * wip(go): inline delete user invite; * wip(go): set scope when creating invite; * wip(go): get user count with membership is ctx is tenant scoped; * wip(go): add methods to upsert and delete org memberships; * wip(go): add global valid tenant and org roles map; * wip(go): add scope query in user invite get by email; * wip(go): add support for upsert and delete of org and tenant membership both; * wip(go): infer scope for user invite apis; * wip(go): wire ctx in get user invite by email; * wip(go): allow no scope headers to be passed in infer scope apis; * wip(go): add validation and checks for org-scoped invite api handlers; * wip(go): wire ctx to is pending user check; * wip(go): infer scope for pending user apis; * wip(go): set default role based on ctx scope; * wip(go): move mfa to tenant membership table; move account status to membership tables; * wip(go): migrate account status and mfa settings; * wip(go): update account status on sync; * wip(go): use update mfa to update mfa setting; * wip(go): set new membership settings on user creation; * wip(go): use membership where required; * wip(go): wire ctx on key unlink refs; * wip(go): use r.Context directly without reinitializing db; * wip(go): wire ctx in acls; * wip(go): add tenant scoped primary key constructor; * wip(go): use tenant scoped primary key for acls, dns, extclients and tags; * wip(go): wire ctx for get groups in network; * wip(go): wire ctx for get groups; * wip(go): scope non-uuid group ids by tenant id; * wip(go): migrate non-uuid group ids to scoped ids; * wip(go): don't expect scope id in invite validation and signup apis; * wip(go): wire ctx for get matching user; allow org scope when querying by external idp id; * wip(go): add mfa to org; * wip(go): scope preauth token; * wip(go): add fallback for older auth tokens; * wip(go): wire ctx for preauth token creation; * wip(go): migrate mfa settings; * wip(go): resolve mfa settings for inherited auth; * wip(go): update mfa for org; * wip(go): infer scope for totp apis; * wip(go): use ctx when passed; * wip(go): infer scope for list user apis; * wip(go): ensure set primary keys don't cause errors during fetch; * wip(go): make primary key for integrations uuid and store provider in separate key; * wip(go): migrate integration ids; * wip(go): use provider instead of id everywhere; * wip(go): remove gorm column; * wip(go): wire ctx for igw egress creation; * wip(go): wire ctx for igw egress update; * wip(go): wire ctx for host registration; * wip(go): wire ctx for host registration; * wip(go): merge errors; * wip(go): merge errors; * wip(go): wire ctx; remove default scope usages; * wip(go): use get with membership; * wip(go): org scope license validation; * wip(go): remove debug log; * wip(go): remove scoping on list roles; * wip(go): ignore invite creation if already exists; * wip(go): add is msp func to check if deployment is msp deployment; * wip(go): ensure username is email for msp users; * wip(go): add email validated check on user table; * wip(go): mark email validated when oauth user; * wip(go): handle gorm error; * wip(go): add tenant id to siem grpc schema; * wip(go): read tenant from scoped ctx; * wip(go): pass scoped ctx to siem grpc client; * wip(go): remove unused publisher; * wip(go): publish all tenant exporter updates on pull; * wip(go): fix wg usage; * wip(go): tenant scope the ctx when getting server info; * wip(go): add tenant id to enrollment key token; * wip(go): make host register tenant scope; * wip(go): add default settings; * wip(go): use correct checks for is msp tenant; * wip(go): add email validation extension; * wip(go): add pages for validation check; * wip(go): use extension for validating user; * wip(go): add invite type; * wip(go): create only one validation invite per user email; * wip(go): add validate email handler; * wip(go): add email builder; * wip(go): add user invite type migration logic; * wip(go): only log email send failures; * wip(go): add tenant id to flows table; * wip(go): add func to check if connected to clickhouse; * wip(go): migrate clickhouse; * wip(go): add tenant id to mdm and edr state; * wip(go): add delete methods for tables; * wip(go): add tenant teardown method; * wip(go): handle tenant deletion; * wip(go): clean up flow logs on tenant deletion; * wip(go): add update org membership role id method; * wip(go): add transfer org ownership api; * wip(go): move audit logs retention period to org settings; * wip(go):get retention period from org settings; * wip(go): remove old audit logs usage; * wip(go): migrate audit logs retention period; * wip(go): unmarshal decodes nums as float64; * wip(go): run flow logs cleanup for all tenants in one go; * wip(go): add audit logs retention period and smtp settings to tenant settings table for non-msp tenants; * wip(go): resolve retention period and smtp settings correctly; * wip(go): handle retention period and smtp settings updates and fetches correctly; * wip(go): overlay retention period and smtp settings on server settings;; * wip(go): upsert overlayed settings; * wip(go): add azure tenant id to org settings; * wip(go): tenant scope list roles; * wip(go): add noop creds; * wip(go): use first instead of find; * wip(go): use correct roles when transfering ownership; * wip(go): delete access tokens only if tenant scoped; * wip(go): make delete user work for both org and tenant scope; * wip(go): delete extclients only if tenant scoped; * wip(go): check if caller is allowed to delete user; * wip(go): infer scope for update user; * wip(go): prevent org-owner elevation; * wip(go): permissions check for update; * wip(go): delete user access tokens only if tenant scope; * wip(go): delete extclients only if tenant scope; * wip(go): move upsert membership in upsert user; * wip(go): set api host when constructing server config; * wip(go): fix static errors; * wip(go): fix test cases; * wip(go): use correct get ns order; * wip(go): use correct get pending host order; * wip(go): use correct get access token order; * wip(go): use correct get event order; * wip(go): return error response on emqx user creation error; * wip(go): store correct value in cache; * wip(go): return better error response; * wip(go): return better error response; * wip(go): use cache when listing egress acls; * wip(go): wire ctx in UserHasNetworkGroupAccess; * wip(go): remove double scope wrapping; * wip(go): add security check for list and get org; * wip(go): ratelimit discover api; * wip(go): expect sole org id in get and create org owner; * wip(go): wrap create org owner in tx;
632 lines
19 KiB
Go
632 lines
19 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gravitl/netmaker/db"
|
|
"github.com/gravitl/netmaker/logger"
|
|
"github.com/gravitl/netmaker/logic"
|
|
"github.com/gravitl/netmaker/middleware"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/mq"
|
|
"github.com/gravitl/netmaker/schema"
|
|
"github.com/gravitl/netmaker/scope"
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
"gorm.io/datatypes"
|
|
)
|
|
|
|
func dnsHandlers(r *mux.Router) {
|
|
|
|
r.HandleFunc("/api/dns", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(getAllDNS)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/dns/adm/{network}/nodes", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(getNodeDNS)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/dns/adm/{network}/custom", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(getCustomDNS)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/dns/adm/{network}", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(getDNS)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/dns/adm/{network}/sync", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(syncDNS)))).
|
|
Methods(http.MethodPost)
|
|
r.HandleFunc("/api/dns/{network}", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(createDNS)))).
|
|
Methods(http.MethodPost)
|
|
r.HandleFunc("/api/dns/adm/pushdns", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(pushDNS)))).
|
|
Methods(http.MethodPost)
|
|
r.HandleFunc("/api/dns/{network}/{domain}", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(deleteDNS)))).
|
|
Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/v1/nameserver", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(createNs)))).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/v1/nameserver", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(listNs)))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/nameserver", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(updateNs)))).Methods(http.MethodPut)
|
|
r.HandleFunc("/api/v1/nameserver", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(deleteNs)))).Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/v1/nameserver/global", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(getGlobalNs)))).Methods(http.MethodGet)
|
|
}
|
|
|
|
// @Summary List Global Nameservers
|
|
// @Router /api/v1/nameserver/global [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Success 200 {object} map[string]schema.Nameserver
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getGlobalNs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, logic.GlobalNsList, "fetched nameservers")
|
|
}
|
|
|
|
// @Summary Create Nameserver
|
|
// @Router /api/v1/nameserver [post]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param body body models.NameserverReq true "Nameserver request body"
|
|
// @Success 200 {object} schema.Nameserver
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func createNs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var req schema.Nameserver
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
if err != nil {
|
|
logger.Log(0, "error decoding request body: ",
|
|
err.Error())
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
if err := logic.ValidateNameserverReq(r.Context(), &req); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
if req.Tags == nil {
|
|
req.Tags = make(datatypes.JSONMap)
|
|
}
|
|
if req.Nodes == nil {
|
|
req.Nodes = make(datatypes.JSONMap)
|
|
}
|
|
if gNs, ok := logic.GlobalNsList[req.Name]; ok {
|
|
req.Servers = gNs.IPs
|
|
}
|
|
if !servercfg.IsPro {
|
|
req.Tags = datatypes.JSONMap{
|
|
"*": struct{}{},
|
|
}
|
|
}
|
|
if req.MatchAll {
|
|
req.Domains = []schema.NameserverDomain{
|
|
{
|
|
Domain: ".",
|
|
},
|
|
}
|
|
}
|
|
if req.Fallback {
|
|
for _, domain := range req.Domains {
|
|
if domain.IsADDomain {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot configure ad domain for fallback nameservers"), "badrequest"))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
ns := schema.Nameserver{
|
|
ID: uuid.New().String(),
|
|
TenantID: scope.ID(r.Context()),
|
|
Name: req.Name,
|
|
NetworkID: req.NetworkID,
|
|
Description: req.Description,
|
|
Fallback: req.Fallback,
|
|
Servers: req.Servers,
|
|
MatchAll: req.MatchAll,
|
|
Domains: req.Domains,
|
|
Tags: req.Tags,
|
|
Nodes: req.Nodes,
|
|
Status: true,
|
|
CreatedBy: r.Header.Get("user"),
|
|
CreatedAt: time.Now().UTC(),
|
|
}
|
|
err = ns.Create(db.WithContext(r.Context()))
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
r,
|
|
logic.FormatError(errors.New("error creating nameserver "+err.Error()), logic.Internal),
|
|
)
|
|
return
|
|
}
|
|
logic.LogEvent(r.Context(), &models.Event{
|
|
Action: schema.Create,
|
|
Source: models.Subject{
|
|
ID: r.Header.Get("user"),
|
|
Name: r.Header.Get("user"),
|
|
Type: schema.UserSub,
|
|
},
|
|
TriggeredBy: r.Header.Get("user"),
|
|
Target: models.Subject{
|
|
ID: ns.ID,
|
|
Name: ns.Name,
|
|
Type: schema.NameserverSub,
|
|
},
|
|
NetworkID: schema.NetworkID(ns.NetworkID),
|
|
Origin: schema.Dashboard,
|
|
})
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponseWithJson(w, r, ns, "created nameserver")
|
|
}
|
|
|
|
// @Summary List Nameservers
|
|
// @Router /api/v1/nameserver [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network query string true "Network identifier"
|
|
// @Success 200 {array} schema.Nameserver
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func listNs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
network := r.URL.Query().Get("network")
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
ns := schema.Nameserver{NetworkID: network}
|
|
list, err := ns.ListByNetwork(db.WithContext(r.Context()))
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
r,
|
|
logic.FormatError(errors.New("error listing nameservers "+err.Error()), "internal"),
|
|
)
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponseWithJson(w, r, list, "fetched nameservers")
|
|
}
|
|
|
|
// @Summary Update Nameserver
|
|
// @Router /api/v1/nameserver [put]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param body body models.NameserverReq true "Nameserver request body"
|
|
// @Success 200 {object} schema.Nameserver
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func updateNs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var updateNs schema.Nameserver
|
|
err := json.NewDecoder(r.Body).Decode(&updateNs)
|
|
if err != nil {
|
|
logger.Log(0, "error decoding request body: ",
|
|
err.Error())
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
|
|
if err := logic.ValidateNameserverReq(r.Context(), &updateNs); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
|
|
if updateNs.Fallback {
|
|
for _, domain := range updateNs.Domains {
|
|
if domain.IsADDomain {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot configure ad domain for fallback nameservers"), "badrequest"))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if updateNs.Tags == nil {
|
|
updateNs.Tags = make(datatypes.JSONMap)
|
|
}
|
|
if updateNs.Nodes == nil {
|
|
updateNs.Nodes = make(datatypes.JSONMap)
|
|
}
|
|
|
|
ns := schema.Nameserver{ID: updateNs.ID}
|
|
err = ns.Get(db.WithContext(r.Context()))
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
var updateStatus bool
|
|
var updateMatchAll bool
|
|
var updateFallback bool
|
|
if updateNs.Status != ns.Status {
|
|
updateStatus = true
|
|
}
|
|
if updateNs.MatchAll != ns.MatchAll {
|
|
updateMatchAll = true
|
|
}
|
|
if updateNs.Fallback != ns.Fallback {
|
|
updateFallback = true
|
|
}
|
|
event := &models.Event{
|
|
Action: schema.Update,
|
|
Source: models.Subject{
|
|
ID: r.Header.Get("user"),
|
|
Name: r.Header.Get("user"),
|
|
Type: schema.UserSub,
|
|
},
|
|
TriggeredBy: r.Header.Get("user"),
|
|
Target: models.Subject{
|
|
ID: ns.ID,
|
|
Name: updateNs.Name,
|
|
Type: schema.NameserverSub,
|
|
},
|
|
Diff: models.Diff{
|
|
Old: ns,
|
|
New: updateNs,
|
|
},
|
|
NetworkID: schema.NetworkID(ns.NetworkID),
|
|
Origin: schema.Dashboard,
|
|
}
|
|
|
|
if !ns.Default {
|
|
if updateNs.MatchAll {
|
|
updateNs.Domains = []schema.NameserverDomain{
|
|
{
|
|
Domain: ".",
|
|
},
|
|
}
|
|
}
|
|
ns.Servers = updateNs.Servers
|
|
ns.Tags = updateNs.Tags
|
|
ns.Domains = updateNs.Domains
|
|
ns.Description = updateNs.Description
|
|
ns.Name = updateNs.Name
|
|
ns.Nodes = updateNs.Nodes
|
|
ns.UpdatedAt = time.Now().UTC()
|
|
|
|
err = ns.Update(r.Context())
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
r,
|
|
logic.FormatError(errors.New("error creating egress resource"+err.Error()), "internal"),
|
|
)
|
|
return
|
|
}
|
|
|
|
if updateMatchAll {
|
|
ns.MatchAll = updateNs.MatchAll
|
|
ns.UpdateMatchAll(r.Context())
|
|
}
|
|
|
|
if updateFallback {
|
|
ns.Fallback = updateNs.Fallback
|
|
ns.UpdateFallback(r.Context())
|
|
}
|
|
}
|
|
|
|
if updateStatus {
|
|
ns.Status = updateNs.Status
|
|
ns.UpdateStatus(r.Context())
|
|
}
|
|
logic.LogEvent(r.Context(), event)
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponseWithJson(w, r, ns, "updated nameserver")
|
|
}
|
|
|
|
// @Summary Delete Nameserver Resource
|
|
// @Router /api/v1/nameserver [delete]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id query string true "Nameserver ID"
|
|
// @Success 200 {string} string
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func deleteNs(w http.ResponseWriter, r *http.Request) {
|
|
|
|
id := r.URL.Query().Get("id")
|
|
if id == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("id is required"), "badrequest"))
|
|
return
|
|
}
|
|
ns := schema.Nameserver{ID: id}
|
|
err := ns.Get(db.WithContext(r.Context()))
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
|
return
|
|
}
|
|
if ns.Default {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot delete default nameservers"), logic.BadReq))
|
|
return
|
|
}
|
|
err = ns.Delete(db.WithContext(r.Context()))
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.Internal))
|
|
return
|
|
}
|
|
logic.LogEvent(r.Context(), &models.Event{
|
|
Action: schema.Delete,
|
|
Source: models.Subject{
|
|
ID: r.Header.Get("user"),
|
|
Name: r.Header.Get("user"),
|
|
Type: schema.UserSub,
|
|
},
|
|
TriggeredBy: r.Header.Get("user"),
|
|
Target: models.Subject{
|
|
ID: ns.ID,
|
|
Name: ns.Name,
|
|
Type: schema.NameserverSub,
|
|
},
|
|
NetworkID: schema.NetworkID(ns.NetworkID),
|
|
Origin: schema.Dashboard,
|
|
Diff: models.Diff{
|
|
Old: ns,
|
|
New: nil,
|
|
},
|
|
})
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted nameserver resource")
|
|
}
|
|
|
|
// @Summary Gets node DNS entries associated with a network
|
|
// @Router /api/dns/adm/{network}/nodes [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Success 200 {array} models.DNSEntry
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getNodeDNS(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var dns []models.DNSEntry
|
|
var params = mux.Vars(r)
|
|
network := params["network"]
|
|
dns, err := logic.GetNodeDNS(r.Context(), network)
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"),
|
|
fmt.Sprintf("failed to get node DNS entries for network [%s]: %v", network, err))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(dns)
|
|
}
|
|
|
|
// @Summary Get all DNS entries
|
|
// @Router /api/dns [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Success 200 {array} models.DNSEntry
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getAllDNS(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
dns, err := logic.GetAllDNS(r.Context())
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"), "failed to get all DNS entries: ", err.Error())
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
logic.SortDNSEntrys(dns[:])
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(dns)
|
|
}
|
|
|
|
// @Summary Gets custom DNS entries associated with a network
|
|
// @Router /api/dns/adm/{network}/custom [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Success 200 {array} models.DNSEntry
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getCustomDNS(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var dns []models.DNSEntry
|
|
var params = mux.Vars(r)
|
|
network := params["network"]
|
|
dns, err := logic.GetCustomDNS(r.Context(), network)
|
|
if err != nil {
|
|
logger.Log(
|
|
0,
|
|
r.Header.Get("user"),
|
|
fmt.Sprintf(
|
|
"failed to get custom DNS entries for network [%s]: %v",
|
|
network,
|
|
err.Error(),
|
|
),
|
|
)
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(dns)
|
|
}
|
|
|
|
// @Summary Get all DNS entries associated with the network
|
|
// @Router /api/dns/adm/{network} [get]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Success 200 {array} models.DNSEntry
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getDNS(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var dns []models.DNSEntry
|
|
var params = mux.Vars(r)
|
|
network := params["network"]
|
|
dns, err := logic.GetDNS(r.Context(), network)
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"),
|
|
fmt.Sprintf("failed to get all DNS entries for network [%s]: %v", network, err.Error()))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(dns)
|
|
}
|
|
|
|
// @Summary Create a new DNS entry
|
|
// @Router /api/dns/{network} [post]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Param body body models.DNSEntry true "DNS entry details"
|
|
// @Success 200 {object} models.DNSEntry
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func createDNS(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var entry models.DNSEntry
|
|
var params = mux.Vars(r)
|
|
netID := params["network"]
|
|
|
|
_ = json.NewDecoder(r.Body).Decode(&entry)
|
|
entry.Network = params["network"]
|
|
|
|
err := logic.ValidateDNSCreate(r.Context(), entry)
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"),
|
|
fmt.Sprintf("invalid DNS entry %+v: %v", entry, err))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
entry, err = logic.CreateDNS(r.Context(), entry)
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"),
|
|
fmt.Sprintf("Failed to create DNS entry %+v: %v", entry, err))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
if logic.GetManageDNS(r.Context()) {
|
|
mq.SendDNSSyncByNetwork(r.Context(), netID)
|
|
}
|
|
|
|
logger.Log(1, "new DNS record added:", entry.Name)
|
|
logger.Log(2, r.Header.Get("user"),
|
|
fmt.Sprintf("DNS entry is set: %+v", entry))
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(entry)
|
|
}
|
|
|
|
// @Summary Delete a DNS entry
|
|
// @Router /api/dns/{network}/{domain} [delete]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Param domain path string true "Domain Name"
|
|
// @Success 200 {string} string
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func deleteDNS(w http.ResponseWriter, r *http.Request) {
|
|
// Set header
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// get params
|
|
var params = mux.Vars(r)
|
|
domain := params["domain"]
|
|
netID := params["network"]
|
|
entrytext := domain + "." + netID
|
|
domain, _ = strings.CutSuffix(domain, "."+logic.GetServerSettings(r.Context()).DefaultDomain)
|
|
err := logic.DeleteDNS(r.Context(), domain, netID)
|
|
|
|
if err != nil {
|
|
logger.Log(0, "failed to delete dns entry: ", entrytext)
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
logger.Log(1, "deleted dns entry: ", entrytext)
|
|
|
|
if logic.GetManageDNS(r.Context()) {
|
|
mq.SendDNSSyncByNetwork(r.Context(), netID)
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(entrytext + " deleted.")
|
|
|
|
}
|
|
|
|
// @Summary Push DNS entries to nameserver
|
|
// @Router /api/dns/adm/pushdns [post]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Success 200 {string} string "DNS Pushed to CoreDNS"
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func pushDNS(w http.ResponseWriter, r *http.Request) {
|
|
// Set header
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if !servercfg.IsDNSMode() {
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
r,
|
|
logic.FormatError(errors.New("DNS Mode is set to off"), "badrequest"),
|
|
)
|
|
return
|
|
}
|
|
|
|
// TODO: deprecate API. does nothing.
|
|
|
|
logger.Log(1, r.Header.Get("user"), "pushed DNS updates to nameserver")
|
|
json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
|
|
}
|
|
|
|
// @Summary Sync DNS entries for a given network
|
|
// @Router /api/dns/adm/{network}/sync [post]
|
|
// @Tags DNS
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network path string true "Network identifier"
|
|
// @Success 200 {string} string "DNS Sync completed successfully"
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func syncDNS(w http.ResponseWriter, r *http.Request) {
|
|
// Set header
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if !logic.GetManageDNS(r.Context()) {
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
r,
|
|
logic.FormatError(errors.New("manage DNS is set to false"), "badrequest"),
|
|
)
|
|
return
|
|
}
|
|
var params = mux.Vars(r)
|
|
netID := params["network"]
|
|
k, err := logic.GetDNS(r.Context(), netID)
|
|
if err == nil && len(k) > 0 {
|
|
err = mq.PushSyncDNS(k)
|
|
}
|
|
|
|
if err != nil {
|
|
logger.Log(0, r.Header.Get("user"),
|
|
fmt.Sprintf("Failed to Sync DNS entries to network %s: %v", netID, err))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
logger.Log(1, r.Header.Get("user"), "DNS Sync complelted successfully")
|
|
json.NewEncoder(w).Encode("DNS Sync completed successfully")
|
|
}
|