mirror of
https://github.com/gravitl/netmaker.git
synced 2026-10-12 02:15:06 +00:00
* wip(go): use default jwt for org tokens; * wip(go): detach request context from goroutine context; * wip(go): allow single tenant creation; * wip(go): remove debug logs; * wip(go): derive host tenant id from enrollment key tenant id; * wip(go): skip server settings migration for new deployment; * 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; * NM-295: publish TCP uplink flags and peer tcp_proxy_endpoint Add gateway tcp_proxy_enabled/listen port and client use_tcp_uplink, and populate PeerIDs on host-level pulls so clients receive dial targets. * 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; * NM-295: resolve pr comments * 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; * NM-295: check network hook cleanup time * 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; * NM-295: mv proxy settings to host * NM-295: preserve tcp uplink setting on disconnect/connect * NM-295: show tcpuplink option on exit node gw if enabled * NM-295: fix orphan gateway relayed client nodes --------- Co-authored-by: VishalDalwadi <[email protected]> Co-authored-by: Vishal Dalwadi <[email protected]>
324 lines
11 KiB
Go
324 lines
11 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gravitl/netmaker/db"
|
|
"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"
|
|
)
|
|
|
|
func deviceHandlers(r *mux.Router) {
|
|
r.HandleFunc("/api/v1/device/register", logic.SecurityCheck(false, http.HandlerFunc(registerDevice))).
|
|
Methods(http.MethodPost)
|
|
r.HandleFunc("/api/v1/device/networks", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(getDeviceNetworks)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/join", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(joinDeviceNetwork)))).
|
|
Methods(http.MethodPost)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/leave", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(leaveDeviceNetwork)))).
|
|
Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/cancel", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(cancelDeviceNetworkJoin)))).
|
|
Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/exit_nodes", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(listDeviceExitNodes)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/exit_node", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(getDeviceExitNode)))).
|
|
Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/device/networks/{network}/exit_node", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(selectDeviceExitNode)))).
|
|
Methods(http.MethodPut, http.MethodPost)
|
|
r.HandleFunc("/api/v1/device/sync", middleware.Scope(scope.TenantScope, logic.SecurityCheck(false, http.HandlerFunc(syncDevice)))).
|
|
Methods(http.MethodPost)
|
|
}
|
|
|
|
func getDeviceUser(w http.ResponseWriter, r *http.Request) (*schema.User, bool) {
|
|
username := r.Header.Get("user")
|
|
if username == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not found in request"), "unauthorized"))
|
|
return nil, false
|
|
}
|
|
user := &schema.User{Username: username}
|
|
if err := user.Get(r.Context()); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
return nil, false
|
|
}
|
|
return user, true
|
|
}
|
|
|
|
func registerDevice(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := getDeviceUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
var newHost schema.Host
|
|
if err := json.NewDecoder(r.Body).Decode(&newHost); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
if newHost.ID == uuid.Nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("host id is required"), "badrequest"))
|
|
return
|
|
}
|
|
if hostID := r.Header.Get(logic.DeviceHostIDHeader); hostID != "" && hostID != newHost.ID.String() {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("host id mismatch"), "badrequest"))
|
|
return
|
|
}
|
|
resp, err := logic.RegisterDevice(r.Context(), user, &newHost)
|
|
if err != nil {
|
|
errType := logic.Internal
|
|
switch {
|
|
case err.Error() == "host does not belong to user",
|
|
err.Error() == "host already registered to another user":
|
|
errType = logic.Forbidden
|
|
case err.Error() == "invalid host id", err.Error() == "missing traffic key":
|
|
errType = logic.BadReq
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponseWithJson(w, r, resp, "device registered")
|
|
}
|
|
|
|
func getDeviceUserAndHost(w http.ResponseWriter, r *http.Request) (*schema.User, *schema.Host, bool) {
|
|
user, ok := getDeviceUser(w, r)
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
username := user.Username
|
|
hostID := r.Header.Get(logic.DeviceHostIDHeader)
|
|
if hostID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("host id is required"), "badrequest"))
|
|
return nil, nil, false
|
|
}
|
|
host, err := logic.VerifyDeviceHostAccess(r.Context(), username, hostID)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
return nil, nil, false
|
|
}
|
|
return user, host, true
|
|
}
|
|
|
|
func getDeviceNetworks(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := getDeviceUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
var host *schema.Host
|
|
hostID := r.Header.Get(logic.DeviceHostIDHeader)
|
|
if hostID != "" {
|
|
if h, err := logic.VerifyDeviceHostAccess(r.Context(), user.Username, hostID); err == nil {
|
|
host = h
|
|
}
|
|
// Ownership mismatch must not block the network list; host-scoped state is omitted without a verified host.
|
|
}
|
|
networks, err := logic.GetDeviceNetworks(r.Context(), user, host)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponseWithJson(w, r, networks, "fetched device networks")
|
|
}
|
|
|
|
func joinDeviceNetwork(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
result, err := logic.JoinDeviceNetwork(r.Context(), user, host, network)
|
|
if err != nil {
|
|
errType := logic.Internal
|
|
switch err.Error() {
|
|
case "user does not have access to network",
|
|
"operation not permitted",
|
|
"JIT access required: please request access from network admin",
|
|
"access blocked: this device doesn't meet security requirements":
|
|
errType = logic.Forbidden
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
if result.Status == models.DeviceJoinStatusPending {
|
|
var httpResponse models.SuccessResponse
|
|
httpResponse.Code = http.StatusAccepted
|
|
httpResponse.Response = result
|
|
httpResponse.Message = "host approval pending for network"
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusAccepted)
|
|
_ = json.NewEncoder(w).Encode(httpResponse)
|
|
return
|
|
}
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponseWithJson(w, r, result, "joined network")
|
|
}
|
|
|
|
func leaveDeviceNetwork(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
if err := logic.LeaveDeviceNetwork(r.Context(), user, host, network); err != nil {
|
|
errType := logic.Internal
|
|
switch err.Error() {
|
|
case "user does not have access to network", "operation not permitted":
|
|
errType = logic.Forbidden
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponse(w, r, "left network")
|
|
}
|
|
|
|
func cancelDeviceNetworkJoin(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
if err := logic.CancelDeviceNetworkJoin(r.Context(), user, host, network); err != nil {
|
|
errType := logic.Internal
|
|
if err.Error() == "no pending join request for network" {
|
|
errType = logic.BadReq
|
|
}
|
|
if err.Error() == "user does not have access to network" || err.Error() == "operation not permitted" {
|
|
errType = logic.Forbidden
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponse(w, r, "join request cancelled")
|
|
}
|
|
|
|
func syncDevice(w http.ResponseWriter, r *http.Request) {
|
|
_, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := logic.SyncDevice(host); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponse(w, r, "sync requested")
|
|
}
|
|
|
|
func listDeviceExitNodes(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
nodes, err := logic.ListDeviceExitNodes(r.Context(), user, host, network)
|
|
if err != nil {
|
|
errType := logic.Internal
|
|
switch err.Error() {
|
|
case "user does not have access to network":
|
|
errType = logic.Forbidden
|
|
case "device is not joined to network", "network is required":
|
|
errType = logic.BadReq
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponseWithJson(w, r, nodes, "fetched exit nodes")
|
|
}
|
|
|
|
func getDeviceExitNode(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
selected, err := logic.GetDeviceSelectedExitNode(r.Context(), user, host, network)
|
|
if err != nil {
|
|
errType := logic.Internal
|
|
switch err.Error() {
|
|
case "user does not have access to network":
|
|
errType = logic.Forbidden
|
|
case "device is not joined to network", "network is required":
|
|
errType = logic.BadReq
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
logic.ReturnSuccessResponseWithJson(w, r, selected, "fetched selected exit node")
|
|
}
|
|
|
|
func selectDeviceExitNode(w http.ResponseWriter, r *http.Request) {
|
|
user, host, ok := getDeviceUserAndHost(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
network := mux.Vars(r)["network"]
|
|
if network == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
var req models.DeviceExitNodeSelectionReq
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
selected, err := logic.SelectDeviceExitNode(r.Context(), user, host, network, req.EgressID, req.UseTcpUplink)
|
|
if err != nil {
|
|
errType := logic.Internal
|
|
switch err.Error() {
|
|
case "user does not have access to network", "user does not have access to this exit node",
|
|
"operation not permitted":
|
|
errType = logic.Forbidden
|
|
case "device is not joined to network", "network is required", "exit node not found",
|
|
"egress is not an active internet exit node in this network",
|
|
"exit node cannot use another exit node",
|
|
"routing node cannot select itself as exit node",
|
|
"gateway nodes cannot be assigned an exit node",
|
|
"node is relayed by a different gateway",
|
|
"internet egress has no routing node":
|
|
errType = logic.BadReq
|
|
default:
|
|
if strings.Contains(err.Error(), "does not have TCP proxy enabled") {
|
|
errType = logic.BadReq
|
|
}
|
|
}
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, errType))
|
|
return
|
|
}
|
|
msg := "exit node selected"
|
|
if req.EgressID == "" {
|
|
msg = "exit node cleared"
|
|
}
|
|
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
|
|
go mq.PublishPeerUpdate(ctx, false)
|
|
logic.ReturnSuccessResponseWithJson(w, r, selected, msg)
|
|
}
|