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 dc84d90be2.
This commit is contained in:
Abhishek K 2024-10-01 17:48:36 +04:00 committed by GitHub
parent 19c6a2f20f
commit 1f9808ff59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 270 additions and 1 deletions

View file

@ -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())
}

View file

@ -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
pro/controllers/rac.go Normal file
View file

@ -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)
}

View file

@ -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]

View file

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

View file

@ -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")
}

View file

@ -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)