Răsfoiți Sursa

NET-1604: New Simplified RAC Apis (#3147)

* ipv6 fix for mobile apps

* simplified RAC APIs

* add response to invite api

* fix get config api

* fix middleware for auth

* add separate controller for rac apis

* Revert "ipv6 fix for mobile apps"

This reverts commit dc84d90be260d0a000fd4575e65e93c98b9279be.
Abhishek K 11 luni în urmă
părinte
comite
1f9808ff59

+ 4 - 0
controllers/middleware.go

@@ -27,6 +27,7 @@ func userMiddleWare(handler http.Handler) http.Handler {
 		r.Header.Set("TARGET_RSRC", "")
 		r.Header.Set("RSRC_TYPE", "")
 		r.Header.Set("TARGET_RSRC_ID", "")
+		r.Header.Set("RAC", "")
 		r.Header.Set("NET_ID", params["network"])
 		if strings.Contains(route, "hosts") || strings.Contains(route, "nodes") {
 			r.Header.Set("TARGET_RSRC", models.HostRsrc.String())
@@ -34,6 +35,9 @@ func userMiddleWare(handler http.Handler) http.Handler {
 		if strings.Contains(route, "dns") {
 			r.Header.Set("TARGET_RSRC", models.DnsRsrc.String())
 		}
+		if strings.Contains(route, "rac") {
+			r.Header.Set("RAC", "true")
+		}
 		if strings.Contains(route, "users") {
 			r.Header.Set("TARGET_RSRC", models.UserRsrc.String())
 		}

+ 10 - 0
models/structs.go

@@ -45,6 +45,16 @@ type UserRemoteGws struct {
 	NetworkAddresses  []string  `json:"network_addresses"`
 }
 
+// UserRAGs - struct for user access gws
+type UserRAGs struct {
+	GwID              string `json:"remote_access_gw_id"`
+	GWName            string `json:"gw_name"`
+	Network           string `json:"network"`
+	Connected         bool   `json:"connected"`
+	IsInternetGateway bool   `json:"is_internet_gateway"`
+	Metadata          string `json:"metadata"`
+}
+
 // UserRemoteGwsReq - struct to hold user remote acccess gws req
 type UserRemoteGwsReq struct {
 	RemoteAccessClientID string `json:"remote_access_clientid"`

+ 14 - 0
pro/controllers/rac.go

@@ -0,0 +1,14 @@
+package controllers
+
+import (
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logic"
+)
+
+func RacHandlers(r *mux.Router) {
+	r.HandleFunc("/api/v1/rac/networks", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworks))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/rac/network/{network}/access_points", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessNetworkGateways))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/rac/access_point/{access_point_id}/config", logic.SecurityCheck(false, http.HandlerFunc(getRemoteAccessGatewayConf))).Methods(http.MethodGet)
+}

+ 213 - 1
pro/controllers/users.go

@@ -263,7 +263,6 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
 		}(invite)
 	}
 	logic.ReturnSuccessResponse(w, r, "triggered user invites")
-
 }
 
 // swagger:route GET /api/v1/users/invites user listUserInvites
@@ -816,6 +815,218 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
 
+// @Summary     Get Users Remote Access Gw Networks.
+// @Router      /api/users/{username}/remote_access_gw [get]
+// @Tags        Users
+// @Param       username path string true "Username to fetch all the gateways with access"
+// @Success     200 {object} map[string][]models.UserRemoteGws
+// @Failure     500 {object} models.ErrorResponse
+func getUserRemoteAccessNetworks(w http.ResponseWriter, r *http.Request) {
+	// set header.
+	w.Header().Set("Content-Type", "application/json")
+	username := r.Header.Get("user")
+	user, err := logic.GetUser(username)
+	if err != nil {
+		logger.Log(0, username, "failed to fetch user: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
+		return
+	}
+	userGws := make(map[string][]models.UserRemoteGws)
+	networks := []models.Network{}
+	networkMap := make(map[string]struct{})
+	userGwNodes := proLogic.GetUserRAGNodes(*user)
+	for _, node := range userGwNodes {
+		network, err := logic.GetNetwork(node.Network)
+		if err != nil {
+			slog.Error("failed to get node network", "error", err)
+			continue
+		}
+		if _, ok := networkMap[network.NetID]; ok {
+			continue
+		}
+		networkMap[network.NetID] = struct{}{}
+		networks = append(networks, network)
+	}
+
+	slog.Debug("returned user gws", "user", username, "gws", userGws)
+	logic.ReturnSuccessResponseWithJson(w, r, networks, "fetched user accessible networks")
+}
+
+// @Summary     Get Users Remote Access Gw Networks.
+// @Router      /api/users/{username}/remote_access_gw [get]
+// @Tags        Users
+// @Param       username path string true "Username to fetch all the gateways with access"
+// @Success     200 {object} map[string][]models.UserRemoteGws
+// @Failure     500 {object} models.ErrorResponse
+func getUserRemoteAccessNetworkGateways(w http.ResponseWriter, r *http.Request) {
+	// set header.
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	username := r.Header.Get("user")
+	user, err := logic.GetUser(username)
+	if err != nil {
+		logger.Log(0, username, "failed to fetch user: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
+		return
+	}
+	network := params["network"]
+	if network == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params network"), "badrequest"))
+		return
+	}
+	userGws := []models.UserRAGs{}
+
+	userGwNodes := proLogic.GetUserRAGNodes(*user)
+	for _, node := range userGwNodes {
+		if node.Network != network {
+			continue
+		}
+
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			continue
+		}
+
+		userGws = append(userGws, models.UserRAGs{
+			GwID:              node.ID.String(),
+			GWName:            host.Name,
+			Network:           node.Network,
+			IsInternetGateway: node.IsInternetGateway,
+			Metadata:          node.Metadata,
+		})
+
+	}
+
+	slog.Debug("returned user gws", "user", username, "gws", userGws)
+	logic.ReturnSuccessResponseWithJson(w, r, userGws, "fetched user accessible gateways in network "+network)
+}
+
+// @Summary     Get Users Remote Access Gw Networks.
+// @Router      /api/users/{username}/remote_access_gw [get]
+// @Tags        Users
+// @Param       username path string true "Username to fetch all the gateways with access"
+// @Success     200 {object} map[string][]models.UserRemoteGws
+// @Failure     500 {object} models.ErrorResponse
+func getRemoteAccessGatewayConf(w http.ResponseWriter, r *http.Request) {
+	// set header.
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	username := r.Header.Get("user")
+	user, err := logic.GetUser(username)
+	if err != nil {
+		logger.Log(0, username, "failed to fetch user: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
+		return
+	}
+	remoteGwID := params["access_point_id"]
+	if remoteGwID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params access_point_id"), "badrequest"))
+		return
+	}
+	var req models.UserRemoteGwsReq
+	err = json.NewDecoder(r.Body).Decode(&req)
+	if err != nil {
+		slog.Error("error decoding request body: ", "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+
+	userGwNodes := proLogic.GetUserRAGNodes(*user)
+	if _, ok := userGwNodes[remoteGwID]; !ok {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access denied"), "forbidden"))
+		return
+	}
+	node, err := logic.GetNodeByID(remoteGwID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch gw node %s, error: %v", remoteGwID, err), "badrequest"))
+		return
+	}
+	host, err := logic.GetHost(node.HostID.String())
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch gw host %s, error: %v", remoteGwID, err), "badrequest"))
+		return
+	}
+	network, err := logic.GetNetwork(node.Network)
+	if err != nil {
+		slog.Error("failed to get node network", "error", err)
+	}
+	var userConf models.ExtClient
+	allextClients, err := logic.GetAllExtClients()
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	for _, extClient := range allextClients {
+		if extClient.Network != network.NetID || extClient.IngressGatewayID != node.ID.String() {
+			continue
+		}
+		if extClient.RemoteAccessClientID == req.RemoteAccessClientID && extClient.OwnerID == username {
+			userConf = extClient
+			userConf.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
+		}
+	}
+	if userConf.ClientID == "" {
+		// create a new conf
+		userConf.OwnerID = user.UserName
+		userConf.RemoteAccessClientID = req.RemoteAccessClientID
+		userConf.IngressGatewayID = node.ID.String()
+
+		// set extclient dns to ingressdns if extclient dns is not explicitly set
+		if (userConf.DNS == "") && (node.IngressDNS != "") {
+			userConf.DNS = node.IngressDNS
+		}
+
+		userConf.Network = node.Network
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			logger.Log(0, r.Header.Get("user"),
+				fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", node.ID, err))
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+		listenPort := logic.GetPeerListenPort(host)
+		if host.EndpointIP.To4() == nil {
+			userConf.IngressGatewayEndpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), listenPort)
+		} else {
+			userConf.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
+		}
+		userConf.Enabled = true
+		parentNetwork, err := logic.GetNetwork(node.Network)
+		if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
+			userConf.Enabled = parentNetwork.DefaultACL == "yes"
+		}
+		if err = logic.CreateExtClient(&userConf); err != nil {
+			slog.Error(
+				"failed to create extclient",
+				"user",
+				r.Header.Get("user"),
+				"network",
+				node.Network,
+				"error",
+				err,
+			)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+	}
+	userGw := models.UserRemoteGws{
+		GwID:              node.ID.String(),
+		GWName:            host.Name,
+		Network:           node.Network,
+		GwClient:          userConf,
+		Connected:         true,
+		IsInternetGateway: node.IsInternetGateway,
+		GwPeerPublicKey:   host.PublicKey.String(),
+		GwListenPort:      logic.GetPeerListenPort(host),
+		Metadata:          node.Metadata,
+		AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
+		NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
+	}
+
+	slog.Debug("returned user gw config", "user", user.UserName, "gws", userGw)
+	logic.ReturnSuccessResponseWithJson(w, r, userGw, "fetched user config to gw "+remoteGwID)
+}
+
 // @Summary     Get Users Remote Access Gw.
 // @Router      /api/users/{username}/remote_access_gw [get]
 // @Tags        Users
@@ -876,6 +1087,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 			network, err := logic.GetNetwork(node.Network)
 			if err != nil {
 				slog.Error("failed to get node network", "error", err)
+				continue
 			}
 
 			gws := userGws[node.Network]

+ 1 - 0
pro/initialize.go

@@ -33,6 +33,7 @@ func InitPro() {
 		proControllers.UserHandlers,
 		proControllers.FailOverHandlers,
 		proControllers.InetHandlers,
+		proControllers.RacHandlers,
 	)
 	controller.ListRoles = proControllers.ListRoles
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {

+ 3 - 0
pro/logic/security.go

@@ -50,6 +50,9 @@ func NetworkPermissionsCheck(username string, r *http.Request) error {
 	if targetRsrc == "" {
 		return errors.New("target rsrc is missing")
 	}
+	if r.Header.Get("RAC") == "true" && r.Method == http.MethodGet {
+		return nil
+	}
 	if netID == "" {
 		return errors.New("network id is missing")
 	}

+ 25 - 0
pro/logic/user_mgmt.go

@@ -511,6 +511,31 @@ func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, n
 	_, ok = rsrcScope[rsrcID]
 	return ok
 }
+
+func DoesUserHaveAccessToRAGNode(user models.User, node models.Node) bool {
+	userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
+	logger.Log(3, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
+	_, allNetAccess := userGwAccessScope["*"]
+	if node.IsIngressGateway && !node.PendingDelete {
+		if allNetAccess {
+			return true
+		} else {
+			gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
+			scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
+			if !ok {
+				if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
+					return false
+				}
+			}
+			if scope.VPNaccess {
+				return true
+			}
+
+		}
+	}
+	return false
+}
+
 func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
 	gws = make(map[string]models.Node)
 	userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)