Jelajahi Sumber

Merge branch 'release-v1.0.0' of https://github.com/gravitl/netmaker into NM-96-v1

abhishek9686 2 minggu lalu
induk
melakukan
8a76566de2

+ 18 - 0
logic/settings.go

@@ -38,6 +38,24 @@ func UpsertServerSettings(s models.ServerSettings) error {
 		s.BasicAuth = true
 	}
 
+	var userFilters []string
+	for _, userFilter := range s.UserFilters {
+		userFilter = strings.TrimSpace(userFilter)
+		if userFilter != "" {
+			userFilters = append(userFilters, userFilter)
+		}
+	}
+	s.UserFilters = userFilters
+
+	var groupFilters []string
+	for _, groupFilter := range s.GroupFilters {
+		groupFilter = strings.TrimSpace(groupFilter)
+		if groupFilter != "" {
+			groupFilters = append(groupFilters, groupFilter)
+		}
+	}
+	s.GroupFilters = groupFilters
+
 	data, err := json.Marshal(s)
 	if err != nil {
 		return err

+ 75 - 5
pro/auth/sync.go

@@ -3,6 +3,10 @@ package auth
 import (
 	"context"
 	"fmt"
+	"strings"
+	"sync"
+	"time"
+
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
@@ -12,9 +16,6 @@ import (
 	"github.com/gravitl/netmaker/pro/idp/google"
 	"github.com/gravitl/netmaker/pro/idp/okta"
 	proLogic "github.com/gravitl/netmaker/pro/logic"
-	"strings"
-	"sync"
-	"time"
 )
 
 var (
@@ -85,15 +86,23 @@ func SyncFromIDP() error {
 	}
 
 	if settings.AuthProvider != "" && idpClient != nil {
-		idpUsers, err = idpClient.GetUsers()
+		idpUsers, err = idpClient.GetUsers(settings.UserFilters)
 		if err != nil {
 			return err
 		}
 
-		idpGroups, err = idpClient.GetGroups()
+		idpGroups, err = idpClient.GetGroups(settings.GroupFilters)
 		if err != nil {
 			return err
 		}
+
+		if len(settings.GroupFilters) > 0 {
+			idpUsers = filterUsersByGroupMembership(idpUsers, idpGroups)
+		}
+
+		if len(settings.UserFilters) > 0 {
+			idpGroups = filterGroupsByMembers(idpGroups, idpUsers)
+		}
 	}
 
 	err = syncUsers(idpUsers)
@@ -316,3 +325,64 @@ func syncGroups(idpGroups []idp.Group) error {
 
 	return nil
 }
+
+func filterUsersByGroupMembership(idpUsers []idp.User, idpGroups []idp.Group) []idp.User {
+	usersMap := make(map[string]int)
+	for i, user := range idpUsers {
+		usersMap[user.ID] = i
+	}
+
+	filteredUsersMap := make(map[string]int)
+	for _, group := range idpGroups {
+		for _, member := range group.Members {
+			if userIdx, ok := usersMap[member]; ok {
+				// user at index `userIdx` is a member of at least one of the
+				// groups in the `idpGroups` list, so we keep it.
+				filteredUsersMap[member] = userIdx
+			}
+		}
+	}
+
+	i := 0
+	filteredUsers := make([]idp.User, len(filteredUsersMap))
+	for _, userIdx := range filteredUsersMap {
+		filteredUsers[i] = idpUsers[userIdx]
+		i++
+	}
+
+	return filteredUsers
+}
+
+func filterGroupsByMembers(idpGroups []idp.Group, idpUsers []idp.User) []idp.Group {
+	usersMap := make(map[string]int)
+	for i, user := range idpUsers {
+		usersMap[user.ID] = i
+	}
+
+	filteredGroupsMap := make(map[int]bool)
+	for i, group := range idpGroups {
+		var members []string
+		for _, member := range group.Members {
+			if _, ok := usersMap[member]; ok {
+				members = append(members, member)
+			}
+
+			if len(members) > 0 {
+				// the group at index `i` has members from the `idpUsers` list,
+				// so we keep it.
+				filteredGroupsMap[i] = true
+				// filter out members that were not provided in the `idpUsers` list.
+				idpGroups[i].Members = members
+			}
+		}
+	}
+
+	i := 0
+	filteredGroups := make([]idp.Group, len(filteredGroupsMap))
+	for groupIdx := range filteredGroupsMap {
+		filteredGroups[i] = idpGroups[groupIdx]
+		i++
+	}
+
+	return filteredGroups
+}

+ 83 - 54
pro/idp/azure/azure.go

@@ -4,10 +4,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/pro/idp"
 	"net/http"
 	"net/url"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/pro/idp"
 )
 
 type Client struct {
@@ -26,89 +27,103 @@ func NewAzureEntraIDClient() *Client {
 	}
 }
 
-func (a *Client) GetUsers() ([]idp.User, error) {
+func (a *Client) GetUsers(filters []string) ([]idp.User, error) {
 	accessToken, err := a.getAccessToken()
 	if err != nil {
 		return nil, err
 	}
 
 	client := &http.Client{}
-	req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/users?$select=id,userPrincipalName,displayName,accountEnabled", nil)
-	if err != nil {
-		return nil, err
+	getUsersURL := "https://graph.microsoft.com/v1.0/users?$select=id,userPrincipalName,displayName,accountEnabled"
+	if len(filters) > 0 {
+		getUsersURL += "&" + buildPrefixFilter("userPrincipalName", filters)
 	}
 
-	req.Header.Add("Authorization", "Bearer "+accessToken)
-	req.Header.Add("Accept", "application/json")
+	var retval []idp.User
+	for getUsersURL != "" {
+		req, err := http.NewRequest("GET", getUsersURL, nil)
+		if err != nil {
+			return nil, err
+		}
 
-	resp, err := client.Do(req)
-	if err != nil {
-		return nil, err
-	}
-	defer func() {
-		_ = resp.Body.Close()
-	}()
+		req.Header.Add("Authorization", "Bearer "+accessToken)
+		req.Header.Add("Accept", "application/json")
 
-	var users getUsersResponse
-	err = json.NewDecoder(resp.Body).Decode(&users)
-	if err != nil {
-		return nil, err
-	}
+		resp, err := client.Do(req)
+		if err != nil {
+			return nil, err
+		}
 
-	retval := make([]idp.User, len(users.Value))
-	for i, user := range users.Value {
-		retval[i] = idp.User{
-			ID:              user.Id,
-			Username:        user.UserPrincipalName,
-			DisplayName:     user.DisplayName,
-			AccountDisabled: !user.AccountEnabled,
+		var users getUsersResponse
+		err = json.NewDecoder(resp.Body).Decode(&users)
+		_ = resp.Body.Close()
+		if err != nil {
+			return nil, err
 		}
+
+		for _, user := range users.Value {
+			retval = append(retval, idp.User{
+				ID:              user.Id,
+				Username:        user.UserPrincipalName,
+				DisplayName:     user.DisplayName,
+				AccountDisabled: !user.AccountEnabled,
+			})
+		}
+
+		getUsersURL = users.NextLink
 	}
 
 	return retval, nil
 }
 
-func (a *Client) GetGroups() ([]idp.Group, error) {
+func (a *Client) GetGroups(filters []string) ([]idp.Group, error) {
 	accessToken, err := a.getAccessToken()
 	if err != nil {
 		return nil, err
 	}
 
 	client := &http.Client{}
-	req, err := http.NewRequest("GET", "https://graph.microsoft.com/v1.0/groups?$select=id,displayName&$expand=members($select=id)", nil)
-	if err != nil {
-		return nil, err
+	getGroupsURL := "https://graph.microsoft.com/v1.0/groups?$select=id,displayName&$expand=members($select=id)"
+	if len(filters) > 0 {
+		getGroupsURL += "&" + buildPrefixFilter("displayName", filters)
 	}
 
-	req.Header.Add("Authorization", "Bearer "+accessToken)
-	req.Header.Add("Accept", "application/json")
+	var retval []idp.Group
+	for getGroupsURL != "" {
+		req, err := http.NewRequest("GET", getGroupsURL, nil)
+		if err != nil {
+			return nil, err
+		}
 
-	resp, err := client.Do(req)
-	if err != nil {
-		return nil, err
-	}
-	defer func() {
-		_ = resp.Body.Close()
-	}()
+		req.Header.Add("Authorization", "Bearer "+accessToken)
+		req.Header.Add("Accept", "application/json")
 
-	var groups getGroupsResponse
-	err = json.NewDecoder(resp.Body).Decode(&groups)
-	if err != nil {
-		return nil, err
-	}
+		resp, err := client.Do(req)
+		if err != nil {
+			return nil, err
+		}
 
-	retval := make([]idp.Group, len(groups.Value))
-	for i, group := range groups.Value {
-		retvalMembers := make([]string, len(group.Members))
-		for j, member := range group.Members {
-			retvalMembers[j] = member.Id
+		var groups getGroupsResponse
+		err = json.NewDecoder(resp.Body).Decode(&groups)
+		_ = resp.Body.Close()
+		if err != nil {
+			return nil, err
 		}
 
-		retval[i] = idp.Group{
-			ID:      group.Id,
-			Name:    group.DisplayName,
-			Members: retvalMembers,
+		for _, group := range groups.Value {
+			retvalMembers := make([]string, len(group.Members))
+			for j, member := range group.Members {
+				retvalMembers[j] = member.Id
+			}
+
+			retval = append(retval, idp.Group{
+				ID:      group.Id,
+				Name:    group.DisplayName,
+				Members: retvalMembers,
+			})
 		}
+
+		getGroupsURL = groups.NextLink
 	}
 
 	return retval, nil
@@ -144,6 +159,18 @@ func (a *Client) getAccessToken() (string, error) {
 	return "", errors.New("failed to get access token")
 }
 
+func buildPrefixFilter(field string, prefixes []string) string {
+	if len(prefixes) == 0 {
+		return ""
+	}
+
+	if len(prefixes) == 1 {
+		return fmt.Sprintf("$filter=startswith(%s,'%s')", field, prefixes[0])
+	}
+
+	return buildPrefixFilter(field, prefixes[1:]) + fmt.Sprintf("%%20or%%20startswith(%s,'%s')", field, prefixes[0])
+}
+
 type getUsersResponse struct {
 	OdataContext string `json:"@odata.context"`
 	Value        []struct {
@@ -152,6 +179,7 @@ type getUsersResponse struct {
 		DisplayName       string `json:"displayName"`
 		AccountEnabled    bool   `json:"accountEnabled"`
 	} `json:"value"`
+	NextLink string `json:"@odata.nextLink"`
 }
 
 type getGroupsResponse struct {
@@ -164,4 +192,5 @@ type getGroupsResponse struct {
 			Id        string `json:"id"`
 		} `json:"members"`
 	} `json:"value"`
+	NextLink string `json:"@odata.nextLink"`
 }

+ 3 - 2
pro/idp/google/google.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/base64"
 	"encoding/json"
+
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/pro/idp"
 	admindir "google.golang.org/api/admin/directory/v1"
@@ -59,7 +60,7 @@ func NewGoogleWorkspaceClient() (*Client, error) {
 	}, nil
 }
 
-func (g *Client) GetUsers() ([]idp.User, error) {
+func (g *Client) GetUsers(filters []string) ([]idp.User, error) {
 	var retval []idp.User
 	err := g.service.Users.List().
 		Customer("my_customer").
@@ -81,7 +82,7 @@ func (g *Client) GetUsers() ([]idp.User, error) {
 	return retval, err
 }
 
-func (g *Client) GetGroups() ([]idp.Group, error) {
+func (g *Client) GetGroups(filters []string) ([]idp.Group, error) {
 	var retval []idp.Group
 	err := g.service.Groups.List().
 		Customer("my_customer").

+ 2 - 2
pro/idp/idp.go

@@ -1,8 +1,8 @@
 package idp
 
 type Client interface {
-	GetUsers() ([]User, error)
-	GetGroups() ([]Group, error)
+	GetUsers(filters []string) ([]User, error)
+	GetGroups(filters []string) ([]Group, error)
 }
 
 type User struct {

+ 3 - 2
pro/idp/okta/okta.go

@@ -3,6 +3,7 @@ package okta
 import (
 	"context"
 	"fmt"
+
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/pro/idp"
 	"github.com/okta/okta-sdk-golang/v5/okta"
@@ -42,7 +43,7 @@ func (o *Client) Verify() error {
 	return err
 }
 
-func (o *Client) GetUsers() ([]idp.User, error) {
+func (o *Client) GetUsers(filters []string) ([]idp.User, error) {
 	var retval []idp.User
 	var allUsersFetched bool
 
@@ -81,7 +82,7 @@ func (o *Client) GetUsers() ([]idp.User, error) {
 	return retval, nil
 }
 
-func (o *Client) GetGroups() ([]idp.Group, error) {
+func (o *Client) GetGroups(filters []string) ([]idp.Group, error) {
 	var retval []idp.Group
 	var allGroupsFetched bool
 

+ 9 - 0
pro/logic/acls.go

@@ -449,6 +449,15 @@ func ListUserPolicies(u models.User) []models.Acl {
 func listPoliciesOfUser(user models.User, netID models.NetworkID) []models.Acl {
 	allAcls := logic.ListAcls()
 	userAcls := []models.Acl{}
+	if _, ok := user.UserGroups[globalNetworksAdminGroupID]; ok {
+		user.UserGroups[GetDefaultNetworkAdminGroupID(netID)] = struct{}{}
+	}
+	if _, ok := user.UserGroups[globalNetworksUserGroupID]; ok {
+		user.UserGroups[GetDefaultNetworkUserGroupID(netID)] = struct{}{}
+	}
+	if user.PlatformRoleID == models.AdminRole || user.PlatformRoleID == models.SuperAdminRole {
+		user.UserGroups[GetDefaultNetworkAdminGroupID(netID)] = struct{}{}
+	}
 	for _, acl := range allAcls {
 		if acl.NetworkID == netID && acl.RuleType == models.UserPolicy {
 			srcMap := logic.ConvAclTagToValueMap(acl.Src)

+ 4 - 1
pro/logic/user_mgmt.go

@@ -729,7 +729,10 @@ func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
 			continue
 		}
 		if user.PlatformRoleID == models.AdminRole || user.PlatformRoleID == models.SuperAdminRole {
-			gws[node.ID.String()] = node
+			if ok, _ := IsUserAllowedToCommunicate(user.UserName, node); ok {
+				gws[node.ID.String()] = node
+				continue
+			}
 		} else {
 			// check if user has network role assigned
 			if roles, ok := user.NetworkRoles[models.NetworkID(node.Network)]; ok && len(roles) > 0 {