[management] Add routing peer counter (#3036)

This commit is contained in:
Pascal Fischer 2024-12-13 11:57:07 +01:00 committed by GitHub
parent 86352f00fc
commit e436c39a86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 344 additions and 88 deletions

View File

@ -29,6 +29,7 @@ import (
"github.com/netbirdio/netbird/management/domain" "github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/geolocation" "github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/integrated_validator" "github.com/netbirdio/netbird/management/server/integrated_validator"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
@ -152,6 +153,7 @@ type AccountManager interface {
DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error
GetNetworksManager() networks.Manager GetNetworksManager() networks.Manager
GetUserManager() users.Manager GetUserManager() users.Manager
GetGroupsManager() groups.Manager
} }
type DefaultAccountManager struct { type DefaultAccountManager struct {
@ -189,6 +191,7 @@ type DefaultAccountManager struct {
metrics telemetry.AppMetrics metrics telemetry.AppMetrics
groupsManager groups.Manager
networksManager networks.Manager networksManager networks.Manager
userManager users.Manager userManager users.Manager
settingsManager settings.Manager settingsManager settings.Manager
@ -268,6 +271,7 @@ func BuildManager(
peersUpdateManager: peersUpdateManager, peersUpdateManager: peersUpdateManager,
idpManager: idpManager, idpManager: idpManager,
networksManager: networks.NewManager(store, permissionsManager), networksManager: networks.NewManager(store, permissionsManager),
groupsManager: groups.NewManager(store, permissionsManager),
userManager: userManager, userManager: userManager,
settingsManager: settingsManager, settingsManager: settingsManager,
permissionsManager: permissionsManager, permissionsManager: permissionsManager,
@ -1737,6 +1741,10 @@ func (am *DefaultAccountManager) GetUserManager() users.Manager {
return am.userManager return am.userManager
} }
func (am *DefaultAccountManager) GetGroupsManager() groups.Manager {
return am.groupsManager
}
// addAllGroup to account object if it doesn't exist // addAllGroup to account object if it doesn't exist
func addAllGroup(account *types.Account) error { func addAllGroup(account *types.Account) error {
if len(account.Groups) == 0 { if len(account.Groups) == 0 {

View File

@ -0,0 +1,99 @@
package groups
import (
"context"
"fmt"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
)
type Manager interface {
GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error)
AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resourceID *types.Resource) error
}
type managerImpl struct {
store store.Store
permissionsManager permissions.Manager
}
func NewManager(store store.Store, permissionsManager permissions.Manager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
}
}
func (m *managerImpl) GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Read)
if err != nil {
return nil, err
}
if !ok {
return nil, err
}
groups, err := m.store.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
if err != nil {
return nil, fmt.Errorf("error getting account groups: %w", err)
}
groupsMap := make(map[string]*types.Group)
for _, group := range groups {
groupsMap[group.ID] = group
}
return groupsMap, nil
}
func (m *managerImpl) AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resource *types.Resource) error {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Write)
if err != nil {
return err
}
if !ok {
return err
}
return m.store.AddResourceToGroup(ctx, accountID, groupID, resource)
}
func ToGroupsInfo(groups map[string]*types.Group, id string) []api.GroupMinimum {
groupsInfo := []api.GroupMinimum{}
groupsChecked := make(map[string]struct{})
for _, group := range groups {
_, ok := groupsChecked[group.ID]
if ok {
continue
}
groupsChecked[group.ID] = struct{}{}
for _, pk := range group.Peers {
if pk == id {
info := api.GroupMinimum{
Id: group.ID,
Name: group.Name,
PeersCount: len(group.Peers),
ResourcesCount: len(group.Resources),
}
groupsInfo = append(groupsInfo, info)
break
}
}
for _, rk := range group.Resources {
if rk.ID == id {
info := api.GroupMinimum{
Id: group.ID,
Name: group.Name,
PeersCount: len(group.Peers),
ResourcesCount: len(group.Resources),
}
groupsInfo = append(groupsInfo, info)
break
}
}
}
return groupsInfo
}

View File

@ -1241,6 +1241,10 @@ components:
items: items:
type: string type: string
example: ch8i4ug6lnn4g9hqv7m0 example: ch8i4ug6lnn4g9hqv7m0
routing_peers_count:
description: Count of routing peers associated with the network
type: integer
example: 2
resources: resources:
description: List of network resource IDs associated with the network description: List of network resource IDs associated with the network
type: array type: array
@ -1251,8 +1255,9 @@ components:
- id - id
- routers - routers
- resources - resources
- routing_peers_count
- $ref: '#/components/schemas/NetworkRequest' - $ref: '#/components/schemas/NetworkRequest'
NetworkResourceRequest: NetworkResourceMinimum:
type: object type: object
properties: properties:
name: name:
@ -1270,6 +1275,19 @@ components:
required: required:
- name - name
- address - address
NetworkResourceRequest:
allOf:
- $ref: '#/components/schemas/NetworkResourceMinimum'
- type: object
properties:
groups:
description: Group IDs containing the resource
type: array
items:
type: string
example: "chacdk86lnnboviihd70"
required:
- groups
NetworkResource: NetworkResource:
allOf: allOf:
- type: object - type: object
@ -1280,10 +1298,16 @@ components:
example: chacdk86lnnboviihd7g example: chacdk86lnnboviihd7g
type: type:
$ref: '#/components/schemas/NetworkResourceType' $ref: '#/components/schemas/NetworkResourceType'
groups:
description: Groups that the resource belongs to
type: array
items:
$ref: '#/components/schemas/GroupMinimum'
required: required:
- id - id
- type - type
- $ref: '#/components/schemas/NetworkResourceRequest' - groups
- $ref: '#/components/schemas/NetworkResourceMinimum'
NetworkResourceType: NetworkResourceType:
description: Network resource type based of the address description: Network resource type based of the address
type: string type: string

View File

@ -532,6 +532,9 @@ type Network struct {
// Routers List of router IDs associated with the network // Routers List of router IDs associated with the network
Routers []string `json:"routers"` Routers []string `json:"routers"`
// RoutingPeersCount Count of routing peers associated with the network
RoutingPeersCount int `json:"routing_peers_count"`
} }
// NetworkRequest defines model for NetworkRequest. // NetworkRequest defines model for NetworkRequest.
@ -551,6 +554,9 @@ type NetworkResource struct {
// Description Network resource description // Description Network resource description
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
// Groups Groups that the resource belongs to
Groups []GroupMinimum `json:"groups"`
// Id Network Resource ID // Id Network Resource ID
Id string `json:"id"` Id string `json:"id"`
@ -561,6 +567,18 @@ type NetworkResource struct {
Type NetworkResourceType `json:"type"` Type NetworkResourceType `json:"type"`
} }
// NetworkResourceMinimum defines model for NetworkResourceMinimum.
type NetworkResourceMinimum struct {
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
Address string `json:"address"`
// Description Network resource description
Description *string `json:"description,omitempty"`
// Name Network resource name
Name string `json:"name"`
}
// NetworkResourceRequest defines model for NetworkResourceRequest. // NetworkResourceRequest defines model for NetworkResourceRequest.
type NetworkResourceRequest struct { type NetworkResourceRequest struct {
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com) // Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or a domain like example.com)
@ -569,6 +587,9 @@ type NetworkResourceRequest struct {
// Description Network resource description // Description Network resource description
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
// Groups Group IDs containing the resource
Groups []string `json:"groups"`
// Name Network resource name // Name Network resource name
Name string `json:"name"` Name string `json:"name"`
} }

View File

@ -94,7 +94,7 @@ func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationMa
routes.AddEndpoints(api.AccountManager, authCfg, router) routes.AddEndpoints(api.AccountManager, authCfg, router)
dns.AddEndpoints(api.AccountManager, authCfg, router) dns.AddEndpoints(api.AccountManager, authCfg, router)
events.AddEndpoints(api.AccountManager, authCfg, router) events.AddEndpoints(api.AccountManager, authCfg, router)
networks.AddEndpoints(api.AccountManager.GetNetworksManager(), api.AccountManager.GetAccountIDFromToken, authCfg, router) networks.AddEndpoints(api.AccountManager.GetNetworksManager(), api.AccountManager.GetGroupsManager(), api.AccountManager.GetAccountIDFromToken, authCfg, router)
return rootRouter, nil return rootRouter, nil
} }

View File

@ -287,13 +287,7 @@ func toGroupResponse(peers []*nbpeer.Peer, group *types.Group) *api.Group {
peersMap[peer.ID] = peer peersMap[peer.ID] = peer
} }
resMap := make(map[string]types.Resource, len(peers))
for _, peer := range peers {
peersMap[peer.ID] = peer
}
peerCache := make(map[string]api.PeerMinimum) peerCache := make(map[string]api.PeerMinimum)
resCache := make(map[string]api.Resource)
gr := api.Group{ gr := api.Group{
Id: group.ID, Id: group.ID,
Name: group.Name, Name: group.Name,
@ -319,16 +313,8 @@ func toGroupResponse(peers []*nbpeer.Peer, group *types.Group) *api.Group {
gr.PeersCount = len(gr.Peers) gr.PeersCount = len(gr.Peers)
for _, res := range group.Resources { for _, res := range group.Resources {
_, ok := resCache[res.ID] resResp := res.ToAPIResponse()
if !ok { gr.Resources = append(gr.Resources, *resResp)
resource, ok := resMap[res.ID]
if !ok {
continue
}
resResp := resource.ToAPIResponse()
resCache[res.ID] = *resResp
gr.Resources = append(gr.Resources, *resResp)
}
} }
gr.ResourcesCount = len(gr.Resources) gr.ResourcesCount = len(gr.Resources)

View File

@ -8,36 +8,42 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/configs" "github.com/netbirdio/netbird/management/server/http/configs"
"github.com/netbirdio/netbird/management/server/http/util" "github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/networks" "github.com/netbirdio/netbird/management/server/networks"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
"github.com/netbirdio/netbird/management/server/networks/types" "github.com/netbirdio/netbird/management/server/networks/types"
"github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/status"
nbtypes "github.com/netbirdio/netbird/management/server/types"
) )
// handler is a handler that returns networks of the account // handler is a handler that returns networks of the account
type handler struct { type handler struct {
networksManager networks.Manager networksManager networks.Manager
groupsManager groups.Manager
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
claimsExtractor *jwtclaims.ClaimsExtractor claimsExtractor *jwtclaims.ClaimsExtractor
} }
func AddEndpoints(networksManager networks.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) { func AddEndpoints(networksManager networks.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
networksHandler := newHandler(networksManager, extractFromToken, authCfg) addRouterEndpoints(networksManager.GetRouterManager(), extractFromToken, authCfg, router)
addResourceEndpoints(networksManager.GetResourceManager(), groupsManager, extractFromToken, authCfg, router)
networksHandler := newHandler(networksManager, groupsManager, extractFromToken, authCfg)
router.HandleFunc("/networks", networksHandler.getAllNetworks).Methods("GET", "OPTIONS") router.HandleFunc("/networks", networksHandler.getAllNetworks).Methods("GET", "OPTIONS")
router.HandleFunc("/networks", networksHandler.createNetwork).Methods("POST", "OPTIONS") router.HandleFunc("/networks", networksHandler.createNetwork).Methods("POST", "OPTIONS")
router.HandleFunc("/networks/{networkId}", networksHandler.getNetwork).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}", networksHandler.getNetwork).Methods("GET", "OPTIONS")
router.HandleFunc("/networks/{networkId}", networksHandler.updateNetwork).Methods("PUT", "OPTIONS") router.HandleFunc("/networks/{networkId}", networksHandler.updateNetwork).Methods("PUT", "OPTIONS")
router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS") router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS")
addRouterEndpoints(networksManager.GetRouterManager(), extractFromToken, authCfg, router)
addResourceEndpoints(networksManager.GetResourceManager(), extractFromToken, authCfg, router)
} }
func newHandler(networksManager networks.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *handler { func newHandler(networksManager networks.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *handler {
return &handler{ return &handler{
networksManager: networksManager, networksManager: networksManager,
groupsManager: groupsManager,
extractFromToken: extractFromToken, extractFromToken: extractFromToken,
claimsExtractor: jwtclaims.NewClaimsExtractor( claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithAudience(authCfg.Audience), jwtclaims.WithAudience(authCfg.Audience),
@ -60,24 +66,25 @@ func (h *handler) getAllNetworks(w http.ResponseWriter, r *http.Request) {
return return
} }
routers, err := h.networksManager.GetRouterManager().GetAllRouterIDsInAccount(r.Context(), accountID, userID) resourceIDs, err := h.networksManager.GetResourceManager().GetAllResourceIDsInAccount(r.Context(), accountID, userID)
if err != nil { if err != nil {
util.WriteError(r.Context(), err, w) util.WriteError(r.Context(), err, w)
return return
} }
resources, err := h.networksManager.GetResourceManager().GetAllResourceIDsInAccount(r.Context(), accountID, userID) groups, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil { if err != nil {
util.WriteError(r.Context(), err, w) util.WriteError(r.Context(), err, w)
return return
} }
var networkResponse []*api.Network routers, err := h.networksManager.GetRouterManager().GetAllRoutersInAccount(r.Context(), accountID, userID)
for _, network := range networks { if err != nil {
networkResponse = append(networkResponse, network.ToAPIResponse(routers[network.ID], resources[network.ID])) util.WriteError(r.Context(), err, w)
return
} }
util.WriteJSONObject(r.Context(), w, networkResponse) util.WriteJSONObject(r.Context(), w, h.generateNetworkResponse(networks, routers, resourceIDs, groups))
} }
func (h *handler) createNetwork(w http.ResponseWriter, r *http.Request) { func (h *handler) createNetwork(w http.ResponseWriter, r *http.Request) {
@ -105,7 +112,7 @@ func (h *handler) createNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse([]string{}, []string{})) util.WriteJSONObject(r.Context(), w, network.ToAPIResponse([]string{}, []string{}, 0))
} }
func (h *handler) getNetwork(w http.ResponseWriter, r *http.Request) { func (h *handler) getNetwork(w http.ResponseWriter, r *http.Request) {
@ -129,13 +136,13 @@ func (h *handler) getNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
routerIDs, resourceIDs, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID) routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
if err != nil { if err != nil {
util.WriteError(r.Context(), err, w) util.WriteError(r.Context(), err, w)
return return
} }
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs)) util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount))
} }
func (h *handler) updateNetwork(w http.ResponseWriter, r *http.Request) { func (h *handler) updateNetwork(w http.ResponseWriter, r *http.Request) {
@ -171,13 +178,13 @@ func (h *handler) updateNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
routerIDs, resourceIDs, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID) routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
if err != nil { if err != nil {
util.WriteError(r.Context(), err, w) util.WriteError(r.Context(), err, w)
return return
} }
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs)) util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount))
} }
func (h *handler) deleteNetwork(w http.ResponseWriter, r *http.Request) { func (h *handler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
@ -204,10 +211,10 @@ func (h *handler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
util.WriteJSONObject(r.Context(), w, util.EmptyObject{}) util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
} }
func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, networkID string) ([]string, []string, error) { func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, networkID string) ([]string, []string, int, error) {
resources, err := h.networksManager.GetResourceManager().GetAllResourcesInNetwork(ctx, accountID, userID, networkID) resources, err := h.networksManager.GetResourceManager().GetAllResourcesInNetwork(ctx, accountID, userID, networkID)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to get resources in network: %w", err) return nil, nil, 0, fmt.Errorf("failed to get resources in network: %w", err)
} }
var resourceIDs []string var resourceIDs []string
@ -217,13 +224,48 @@ func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, ne
routers, err := h.networksManager.GetRouterManager().GetAllRoutersInNetwork(ctx, accountID, userID, networkID) routers, err := h.networksManager.GetRouterManager().GetAllRoutersInNetwork(ctx, accountID, userID, networkID)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to get routers in network: %w", err) return nil, nil, 0, fmt.Errorf("failed to get routers in network: %w", err)
} }
groups, err := h.groupsManager.GetAllGroups(ctx, accountID, userID)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to get groups: %w", err)
}
peerCounter := 0
var routerIDs []string var routerIDs []string
for _, router := range routers { for _, router := range routers {
routerIDs = append(routerIDs, router.ID) routerIDs = append(routerIDs, router.ID)
if router.Peer != "" {
peerCounter++
}
if len(router.PeerGroups) > 0 {
for _, groupID := range router.PeerGroups {
peerCounter += len(groups[groupID].Peers)
}
}
} }
return routerIDs, resourceIDs, nil return routerIDs, resourceIDs, peerCounter, nil
}
func (h *handler) generateNetworkResponse(networks []*types.Network, routers map[string][]*routerTypes.NetworkRouter, resourceIDs map[string][]string, groups map[string]*nbtypes.Group) []*api.Network {
var networkResponse []*api.Network
for _, network := range networks {
routerIDs := []string{}
peerCounter := 0
for _, router := range routers[network.ID] {
routerIDs = append(routerIDs, router.ID)
if router.Peer != "" {
peerCounter++
}
if len(router.PeerGroups) > 0 {
for _, groupID := range router.PeerGroups {
peerCounter += len(groups[groupID].Peers)
}
}
}
networkResponse = append(networkResponse, network.ToAPIResponse(routerIDs, resourceIDs[network.ID], peerCounter))
}
return networkResponse
} }

View File

@ -7,33 +7,37 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/configs" "github.com/netbirdio/netbird/management/server/http/configs"
"github.com/netbirdio/netbird/management/server/http/util" "github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/networks/resources" "github.com/netbirdio/netbird/management/server/networks/resources"
"github.com/netbirdio/netbird/management/server/networks/resources/types" "github.com/netbirdio/netbird/management/server/networks/resources/types"
nbtypes "github.com/netbirdio/netbird/management/server/types"
) )
type resourceHandler struct { type resourceHandler struct {
resourceManager resources.Manager resourceManager resources.Manager
groupsManager groups.Manager
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
claimsExtractor *jwtclaims.ClaimsExtractor claimsExtractor *jwtclaims.ClaimsExtractor
} }
func addResourceEndpoints(resourcesManager resources.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) { func addResourceEndpoints(resourcesManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
resourceHandler := newResourceHandler(resourcesManager, extractFromToken, authCfg) resourceHandler := newResourceHandler(resourcesManager, groupsManager, extractFromToken, authCfg)
router.HandleFunc("/networks/resources", resourceHandler.getAllResourcesInAccount).Methods("GET", "OPTIONS")
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.getAllResourcesInNetwork).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources", resourceHandler.getAllResourcesInNetwork).Methods("GET", "OPTIONS")
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.createResource).Methods("POST", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources", resourceHandler.createResource).Methods("POST", "OPTIONS")
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.getResource).Methods("GET", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.getResource).Methods("GET", "OPTIONS")
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.updateResource).Methods("PUT", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.updateResource).Methods("PUT", "OPTIONS")
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.deleteResource).Methods("DELETE", "OPTIONS") router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.deleteResource).Methods("DELETE", "OPTIONS")
router.HandleFunc("/networks/resources", resourceHandler.getAllResourcesInAccount).Methods("GET", "OPTIONS")
} }
func newResourceHandler(resourceManager resources.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *resourceHandler { func newResourceHandler(resourceManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *resourceHandler {
return &resourceHandler{ return &resourceHandler{
resourceManager: resourceManager, resourceManager: resourceManager,
groupsManager: groupsManager,
extractFromToken: extractFromToken, extractFromToken: extractFromToken,
claimsExtractor: jwtclaims.NewClaimsExtractor( claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithAudience(authCfg.Audience), jwtclaims.WithAudience(authCfg.Audience),
@ -57,9 +61,16 @@ func (h *resourceHandler) getAllResourcesInNetwork(w http.ResponseWriter, r *htt
return return
} }
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
var resourcesResponse []*api.NetworkResource var resourcesResponse []*api.NetworkResource
for _, resource := range resources { for _, resource := range resources {
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse()) groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
} }
util.WriteJSONObject(r.Context(), w, resourcesResponse) util.WriteJSONObject(r.Context(), w, resourcesResponse)
@ -78,9 +89,16 @@ func (h *resourceHandler) getAllResourcesInAccount(w http.ResponseWriter, r *htt
return return
} }
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
var resourcesResponse []*api.NetworkResource var resourcesResponse []*api.NetworkResource
for _, resource := range resources { for _, resource := range resources {
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse()) groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
} }
util.WriteJSONObject(r.Context(), w, resourcesResponse) util.WriteJSONObject(r.Context(), w, resourcesResponse)
@ -112,7 +130,26 @@ func (h *resourceHandler) createResource(w http.ResponseWriter, r *http.Request)
return return
} }
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse()) res := nbtypes.Resource{
ID: resource.ID,
Type: resource.Type.String(),
}
for _, groupID := range req.Groups {
err = h.groupsManager.AddResourceToGroup(r.Context(), accountID, userID, groupID, &res)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
}
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
} }
func (h *resourceHandler) getResource(w http.ResponseWriter, r *http.Request) { func (h *resourceHandler) getResource(w http.ResponseWriter, r *http.Request) {
@ -131,7 +168,14 @@ func (h *resourceHandler) getResource(w http.ResponseWriter, r *http.Request) {
return return
} }
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse()) grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
} }
func (h *resourceHandler) updateResource(w http.ResponseWriter, r *http.Request) { func (h *resourceHandler) updateResource(w http.ResponseWriter, r *http.Request) {
@ -161,7 +205,26 @@ func (h *resourceHandler) updateResource(w http.ResponseWriter, r *http.Request)
return return
} }
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse()) res := nbtypes.Resource{
ID: resource.ID,
Type: resource.Type.String(),
}
for _, groupID := range req.Groups {
err = h.groupsManager.AddResourceToGroup(r.Context(), accountID, userID, groupID, &res)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
}
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
} }
func (h *resourceHandler) deleteResource(w http.ResponseWriter, r *http.Request) { func (h *resourceHandler) deleteResource(w http.ResponseWriter, r *http.Request) {

View File

@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/http/api" "github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/configs" "github.com/netbirdio/netbird/management/server/http/configs"
"github.com/netbirdio/netbird/management/server/http/util" "github.com/netbirdio/netbird/management/server/http/util"
@ -71,7 +72,7 @@ func (h *Handler) getPeer(ctx context.Context, account *types.Account, peerID, u
} }
dnsDomain := h.accountManager.GetDNSDomain() dnsDomain := h.accountManager.GetDNSDomain()
groupsInfo := toGroupsInfo(account.Groups, peer.ID) groupsInfo := groups.ToGroupsInfo(account.Groups, peer.ID)
validPeers, err := h.accountManager.GetValidatedPeers(account) validPeers, err := h.accountManager.GetValidatedPeers(account)
if err != nil { if err != nil {
@ -115,7 +116,7 @@ func (h *Handler) updatePeer(ctx context.Context, account *types.Account, userID
} }
dnsDomain := h.accountManager.GetDNSDomain() dnsDomain := h.accountManager.GetDNSDomain()
groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID) groupMinimumInfo := groups.ToGroupsInfo(account.Groups, peer.ID)
validPeers, err := h.accountManager.GetValidatedPeers(account) validPeers, err := h.accountManager.GetValidatedPeers(account)
if err != nil { if err != nil {
@ -200,8 +201,8 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
} }
groupsMap := map[string]*types.Group{} groupsMap := map[string]*types.Group{}
groups, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID) grps, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
for _, group := range groups { for _, group := range grps {
groupsMap[group.ID] = group groupsMap[group.ID] = group
} }
@ -212,7 +213,7 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
util.WriteError(r.Context(), err, w) util.WriteError(r.Context(), err, w)
return return
} }
groupMinimumInfo := toGroupsInfo(groupsMap, peer.ID) groupMinimumInfo := groups.ToGroupsInfo(groupsMap, peer.ID)
respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, 0)) respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, 0))
} }
@ -324,30 +325,6 @@ func peerToAccessiblePeer(peer *nbpeer.Peer, dnsDomain string) api.AccessiblePee
} }
} }
func toGroupsInfo(groups map[string]*types.Group, peerID string) []api.GroupMinimum {
groupsInfo := []api.GroupMinimum{}
groupsChecked := make(map[string]struct{})
for _, group := range groups {
_, ok := groupsChecked[group.ID]
if ok {
continue
}
groupsChecked[group.ID] = struct{}{}
for _, pk := range group.Peers {
if pk == peerID {
info := api.GroupMinimum{
Id: group.ID,
Name: group.Name,
PeersCount: len(group.Peers),
}
groupsInfo = append(groupsInfo, info)
break
}
}
}
return groupsInfo
}
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool) *api.Peer { func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool) *api.Peer {
osVersion := peer.Meta.OSVersion osVersion := peer.Meta.OSVersion
if osVersion == "" { if osVersion == "" {

View File

@ -13,6 +13,7 @@ import (
"github.com/netbirdio/netbird/management/domain" "github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/idp" "github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims" "github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/networks" "github.com/netbirdio/netbird/management/server/networks"
@ -123,6 +124,11 @@ func (am *MockAccountManager) GetNetworksManager() networks.Manager {
panic("implement me") panic("implement me")
} }
func (am *MockAccountManager) GetGroupsManager() groups.Manager {
// TODO implement me
panic("implement me")
}
func (am *MockAccountManager) DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error { func (am *MockAccountManager) DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error {
if am.DeleteSetupKeyFunc != nil { if am.DeleteSetupKeyFunc != nil {
return am.DeleteSetupKeyFunc(ctx, accountID, userID, keyID) return am.DeleteSetupKeyFunc(ctx, accountID, userID, keyID)

View File

@ -51,13 +51,14 @@ func NewNetworkResource(accountID, networkID, name, description, address string)
}, nil }, nil
} }
func (n *NetworkResource) ToAPIResponse() *api.NetworkResource { func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource {
return &api.NetworkResource{ return &api.NetworkResource{
Id: n.ID, Id: n.ID,
Name: n.Name, Name: n.Name,
Description: &n.Description, Description: &n.Description,
Type: api.NetworkResourceType(n.Type.String()), Type: api.NetworkResourceType(n.Type.String()),
Address: n.Address, Address: n.Address,
Groups: groups,
} }
} }

View File

@ -15,7 +15,7 @@ import (
type Manager interface { type Manager interface {
GetAllRoutersInNetwork(ctx context.Context, accountID, userID, networkID string) ([]*types.NetworkRouter, error) GetAllRoutersInNetwork(ctx context.Context, accountID, userID, networkID string) ([]*types.NetworkRouter, error)
GetAllRouterIDsInAccount(ctx context.Context, accountID, userID string) (map[string][]string, error) GetAllRoutersInAccount(ctx context.Context, accountID, userID string) (map[string][]*types.NetworkRouter, error)
CreateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) CreateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error)
GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error) GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error)
UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error)
@ -46,7 +46,7 @@ func (m *managerImpl) GetAllRoutersInNetwork(ctx context.Context, accountID, use
return m.store.GetNetworkRoutersByNetID(ctx, store.LockingStrengthShare, accountID, networkID) return m.store.GetNetworkRoutersByNetID(ctx, store.LockingStrengthShare, accountID, networkID)
} }
func (m *managerImpl) GetAllRouterIDsInAccount(ctx context.Context, accountID, userID string) (map[string][]string, error) { func (m *managerImpl) GetAllRoutersInAccount(ctx context.Context, accountID, userID string) (map[string][]*types.NetworkRouter, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read) ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Networks, permissions.Read)
if err != nil { if err != nil {
return nil, status.NewPermissionValidationError(err) return nil, status.NewPermissionValidationError(err)
@ -60,9 +60,9 @@ func (m *managerImpl) GetAllRouterIDsInAccount(ctx context.Context, accountID, u
return nil, fmt.Errorf("failed to get network routers: %w", err) return nil, fmt.Errorf("failed to get network routers: %w", err)
} }
routersMap := make(map[string][]string) routersMap := make(map[string][]*types.NetworkRouter)
for _, router := range routers { for _, router := range routers {
routersMap[router.NetworkID] = append(routersMap[router.NetworkID], router.ID) routersMap[router.NetworkID] = append(routersMap[router.NetworkID], router)
} }
return routersMap, nil return routersMap, nil

View File

@ -22,13 +22,14 @@ func NewNetwork(accountId, name, description string) *Network {
} }
} }
func (n *Network) ToAPIResponse(routerIDs []string, resourceIDs []string) *api.Network { func (n *Network) ToAPIResponse(routerIDs []string, resourceIDs []string, routingPeersCount int) *api.Network {
return &api.Network{ return &api.Network{
Id: n.ID, Id: n.ID,
Name: n.Name, Name: n.Name,
Description: &n.Description, Description: &n.Description,
Routers: routerIDs, Routers: routerIDs,
Resources: resourceIDs, Resources: resourceIDs,
RoutingPeersCount: routingPeersCount,
} }
} }

View File

@ -15,6 +15,7 @@ type Module string
const ( const (
Networks Module = "networks" Networks Module = "networks"
Peers Module = "peers" Peers Module = "peers"
Groups Module = "groups"
) )
type Operation string type Operation string

View File

@ -1070,6 +1070,32 @@ func (s *SqlStore) AddPeerToGroup(ctx context.Context, accountId string, peerId
return nil return nil
} }
func (s *SqlStore) AddResourceToGroup(ctx context.Context, accountId string, groupID string, resource *types.Resource) error {
var group types.Group
result := s.db.Where(accountAndIDQueryCondition, accountId, groupID).First(&group)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return status.NewGroupNotFoundError(groupID)
}
return status.Errorf(status.Internal, "issue finding group: %s", result.Error)
}
for _, res := range group.Resources {
if res.ID == resource.ID {
return nil
}
}
group.Resources = append(group.Resources, *resource)
if err := s.db.Save(&group).Error; err != nil {
return status.Errorf(status.Internal, "issue updating group: %s", err)
}
return nil
}
// GetUserPeers retrieves peers for a user. // GetUserPeers retrieves peers for a user.
func (s *SqlStore) GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error) { func (s *SqlStore) GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error) {
return getRecords[*nbpeer.Peer](s.db.Where("user_id = ?", userID), lockStrength, accountID) return getRecords[*nbpeer.Peer](s.db.Where("user_id = ?", userID), lockStrength, accountID)

View File

@ -98,6 +98,7 @@ type Store interface {
GetPeerLabelsInAccount(ctx context.Context, lockStrength LockingStrength, accountId string) ([]string, error) GetPeerLabelsInAccount(ctx context.Context, lockStrength LockingStrength, accountId string) ([]string, error)
AddPeerToAllGroup(ctx context.Context, accountID string, peerID string) error AddPeerToAllGroup(ctx context.Context, accountID string, peerID string) error
AddPeerToGroup(ctx context.Context, accountId string, peerId string, groupID string) error AddPeerToGroup(ctx context.Context, accountId string, peerId string, groupID string) error
AddResourceToGroup(ctx context.Context, accountId string, groupID string, resource *types.Resource) error
AddPeerToAccount(ctx context.Context, peer *nbpeer.Peer) error AddPeerToAccount(ctx context.Context, peer *nbpeer.Peer) error
GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error) GetPeerByPeerPubKey(ctx context.Context, lockStrength LockingStrength, peerKey string) (*nbpeer.Peer, error)
GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error) GetUserPeers(ctx context.Context, lockStrength LockingStrength, accountID, userID string) ([]*nbpeer.Peer, error)