| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 | package controllerimport (	"encoding/json"	"errors"	"net/http"	"net/url"	"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/models"	"github.com/gravitl/netmaker/mq"	"github.com/gravitl/netmaker/schema")func aclHandlers(r *mux.Router) {	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(getAcls))).		Methods(http.MethodGet)	r.HandleFunc("/api/v1/acls/egress", logic.SecurityCheck(true, http.HandlerFunc(getEgressAcls))).		Methods(http.MethodGet)	r.HandleFunc("/api/v1/acls/policy_types", logic.SecurityCheck(true, http.HandlerFunc(aclPolicyTypes))).		Methods(http.MethodGet)	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(createAcl))).		Methods(http.MethodPost)	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(updateAcl))).		Methods(http.MethodPut)	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(deleteAcl))).		Methods(http.MethodDelete)	r.HandleFunc("/api/v1/acls/debug", logic.SecurityCheck(true, http.HandlerFunc(aclDebug))).		Methods(http.MethodGet)}// @Summary     List Acl Policy types// @Router      /api/v1/acls/policy_types [get]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc aclPolicyTypes(w http.ResponseWriter, r *http.Request) {	resp := models.AclPolicyTypes{		RuleTypes: []models.AclPolicyType{			models.DevicePolicy,			models.UserPolicy,		},		SrcGroupTypes: []models.AclGroupType{			models.UserAclID,			models.UserGroupAclID,			models.NodeTagID,			models.NodeID,		},		DstGroupTypes: []models.AclGroupType{			models.NodeTagID,			models.NodeID,			models.EgressID,			// models.NetmakerIPAclID,			// models.NetmakerSubNetRangeAClID,		},		ProtocolTypes: []models.ProtocolType{			{				Name: models.Any,				AllowedProtocols: []models.Protocol{					models.ALL,				},				PortRange:        "All ports",				AllowPortSetting: false,			},			{				Name: models.Http,				AllowedProtocols: []models.Protocol{					models.TCP,				},				PortRange: "80",			},			{				Name: models.Https,				AllowedProtocols: []models.Protocol{					models.TCP,				},				PortRange: "443",			},			// {			// 	Name: "MySQL",			// 	AllowedProtocols: []models.Protocol{			// 		models.TCP,			// 	},			// 	PortRange: "3306",			// },			// {			// 	Name: "DNS TCP",			// 	AllowedProtocols: []models.Protocol{			// 		models.TCP,			// 	},			// 	PortRange: "53",			// },			// {			// 	Name: "DNS UDP",			// 	AllowedProtocols: []models.Protocol{			// 		models.UDP,			// 	},			// 	PortRange: "53",			// },			{				Name: models.AllTCP,				AllowedProtocols: []models.Protocol{					models.TCP,				},				PortRange: "All ports",			},			{				Name: models.AllUDP,				AllowedProtocols: []models.Protocol{					models.UDP,				},				PortRange: "All ports",			},			{				Name: models.ICMPService,				AllowedProtocols: []models.Protocol{					models.ICMP,				},				PortRange: "",			},			{				Name: models.SSH,				AllowedProtocols: []models.Protocol{					models.TCP,				},				PortRange: "22",			},			{				Name: models.Custom,				AllowedProtocols: []models.Protocol{					models.UDP,					models.TCP,				},				PortRange:        "All ports",				AllowPortSetting: true,			},		},	}	logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched acls types")}func aclDebug(w http.ResponseWriter, r *http.Request) {	nodeID, _ := url.QueryUnescape(r.URL.Query().Get("node"))	peerID, _ := url.QueryUnescape(r.URL.Query().Get("peer"))	peerIsStatic, _ := url.QueryUnescape(r.URL.Query().Get("peer_is_static"))	node, err := logic.GetNodeByID(nodeID)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	var peer models.Node	if peerIsStatic == "true" {		extclient, err := logic.GetExtClient(peerID, node.Network)		if err != nil {			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))			return		}		peer = extclient.ConvertToStaticNode()	} else {		peer, err = logic.GetNodeByID(peerID)		if err != nil {			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))			return		}	}	type resp struct {		IsNodeAllowed bool		IsPeerAllowed bool		Policies      []models.Acl		IngressRules  []models.FwRule		NodeAllPolicy bool		EgressNets    map[string]models.Node	}	allowed, ps := logic.IsNodeAllowedToCommunicate(node, peer, true)	isallowed := logic.IsPeerAllowed(node, peer, true)	re := resp{		IsNodeAllowed: allowed,		IsPeerAllowed: isallowed,		Policies:      ps,	}	if peerIsStatic == "true" {		ingress, err := logic.GetNodeByID(peer.StaticNode.IngressGatewayID)		if err == nil {			re.IngressRules = logic.GetFwRulesOnIngressGateway(ingress)		}	}	logic.ReturnSuccessResponseWithJson(w, r, re, "fetched all acls in the network ")}// @Summary     List Acls in a network// @Router      /api/v1/acls [get]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc getAcls(w http.ResponseWriter, r *http.Request) {	netID := r.URL.Query().Get("network")	if netID == "" {		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network id param is missing"), "badrequest"))		return	}	// check if network exists	_, err := logic.GetNetwork(netID)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	acls, err := logic.ListAclsByNetwork(models.NetworkID(netID))	if err != nil {		logger.Log(0, r.Header.Get("user"), "failed to get all network acl entries: ", err.Error())		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))		return	}	logic.SortAclEntrys(acls[:])	logic.ReturnSuccessResponseWithJson(w, r, acls, "fetched all acls in the network "+netID)}// @Summary     List Egress Acls in a network// @Router      /api/v1/acls [get]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc getEgressAcls(w http.ResponseWriter, r *http.Request) {	eID := r.URL.Query().Get("egress_id")	if eID == "" {		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("egress id param is missing"), "badrequest"))		return	}	e := schema.Egress{ID: eID}	// check if network exists	err := e.Get(db.WithContext(r.Context()))	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	acls, err := logic.ListEgressAcls(eID)	if err != nil {		logger.Log(0, r.Header.Get("user"), "failed to get all network acl entries: ", err.Error())		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))		return	}	logic.SortAclEntrys(acls[:])	logic.ReturnSuccessResponseWithJson(w, r, acls, "fetched acls for egress"+e.Name)}// @Summary     Create Acl// @Router      /api/v1/acls [post]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc createAcl(w http.ResponseWriter, r *http.Request) {	var req models.Acl	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	}	user, err := logic.GetUser(r.Header.Get("user"))	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	err = logic.ValidateCreateAclReq(req)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	acl := req	acl.ID = uuid.New().String()	acl.CreatedBy = user.UserName	acl.CreatedAt = time.Now().UTC()	acl.Default = false	if acl.ServiceType == models.Any {		acl.Port = []string{}		acl.Proto = models.ALL	}	// validate create acl policy	if err := logic.IsAclPolicyValid(acl); err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	err = logic.InsertAcl(acl)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))		return	}	acl, err = logic.GetAcl(acl.ID)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))		return	}	logic.LogEvent(&models.Event{		Action: models.Create,		Source: models.Subject{			ID:   r.Header.Get("user"),			Name: r.Header.Get("user"),			Type: models.UserSub,		},		TriggeredBy: r.Header.Get("user"),		Target: models.Subject{			ID:   acl.ID,			Name: acl.Name,			Type: models.AclSub,		},		NetworkID: acl.NetworkID,		Origin:    models.Dashboard,	})	go mq.PublishPeerUpdate(true)	logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully")}// @Summary     Update Acl// @Router      /api/v1/acls [put]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc updateAcl(w http.ResponseWriter, r *http.Request) {	var updateAcl models.UpdateAclRequest	err := json.NewDecoder(r.Body).Decode(&updateAcl)	if err != nil {		logger.Log(0, "error decoding request body: ",			err.Error())		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	acl, err := logic.GetAcl(updateAcl.ID)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	if err := logic.IsAclPolicyValid(updateAcl.Acl); err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	if updateAcl.Acl.NetworkID != acl.NetworkID {		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy, network id mismatch"), "badrequest"))		return	}	if !acl.Default && updateAcl.NewName != "" {		//check if policy exists with same name		updateAcl.Acl.Name = updateAcl.NewName	}	err = logic.UpdateAcl(updateAcl.Acl, acl)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	logic.LogEvent(&models.Event{		Action: models.Update,		Source: models.Subject{			ID:   r.Header.Get("user"),			Name: r.Header.Get("user"),			Type: models.UserSub,		},		TriggeredBy: r.Header.Get("user"),		Target: models.Subject{			ID:   acl.ID,			Name: acl.Name,			Type: models.AclSub,		},		Diff: models.Diff{			Old: acl,			New: updateAcl.Acl,		},		NetworkID: acl.NetworkID,		Origin:    models.Dashboard,	})	go mq.PublishPeerUpdate(true)	logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name)}// @Summary     Delete Acl// @Router      /api/v1/acls [delete]// @Tags        ACL// @Accept      json// @Success     200 {array} models.SuccessResponse// @Failure     500 {object} models.ErrorResponsefunc deleteAcl(w http.ResponseWriter, r *http.Request) {	aclID, _ := url.QueryUnescape(r.URL.Query().Get("acl_id"))	if aclID == "" {		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("acl id is required"), "badrequest"))		return	}	acl, err := logic.GetAcl(aclID)	if err != nil {		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))		return	}	if acl.Default {		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot delete default policy"), "badrequest"))		return	}	err = logic.DeleteAcl(acl)	if err != nil {		logic.ReturnErrorResponse(w, r,			logic.FormatError(errors.New("cannot delete default policy"), "internal"))		return	}	logic.LogEvent(&models.Event{		Action: models.Delete,		Source: models.Subject{			ID:   r.Header.Get("user"),			Name: r.Header.Get("user"),			Type: models.UserSub,		},		TriggeredBy: r.Header.Get("user"),		Target: models.Subject{			ID:   acl.ID,			Name: acl.Name,			Type: models.AclSub,		},		NetworkID: acl.NetworkID,		Origin:    models.Dashboard,	})	go mq.PublishPeerUpdate(true)	logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)}
 |