Selaa lähdekoodia

NM-61: User group ACL fixes (#3546)

* feat(go): create default acl only for networks that are part of the group;

* feat(go): update acls on user group update and delete;

* feat(go): add migration for existing acls.

* feat(go): check for network roles in migration.
Vishal Dalwadi 1 kuukausi sitten
vanhempi
commit
e4da84aa85
5 muutettua tiedostoa jossa 186 lisäystä ja 6 poistoa
  1. 13 0
      logic/acls.go
  2. 1 0
      logic/user_mgmt.go
  3. 43 0
      migrate/migrate.go
  4. 128 6
      pro/controllers/users.go
  5. 1 0
      pro/initialize.go

+ 13 - 0
logic/acls.go

@@ -1263,6 +1263,19 @@ func ValidateCreateAclReq(req models.Acl) error {
 	// if err != nil {
 	// 	return err
 	// }
+	for _, src := range req.Src {
+		if src.ID == models.UserGroupAclID {
+			userGroup, err := GetUserGroup(models.UserGroupID(src.Value))
+			if err != nil {
+				return err
+			}
+
+			_, ok := userGroup.NetworkRoles[req.NetworkID]
+			if !ok {
+				return fmt.Errorf("user group %s does not have access to network %s", src.Value, req.NetworkID)
+			}
+		}
+	}
 	return nil
 }
 

+ 1 - 0
logic/user_mgmt.go

@@ -95,6 +95,7 @@ var CreateDefaultUserPolicies = func(netID models.NetworkID) {
 		InsertAcl(defaultUserAcl)
 	}
 }
+var ListUserGroups = func() ([]models.UserGroup, error) { return nil, nil }
 var GetUserGroupsInNetwork = func(netID models.NetworkID) (networkGrps map[models.UserGroupID]models.UserGroup) { return }
 var GetUserGroup = func(groupId models.UserGroupID) (userGrps models.UserGroup, err error) { return }
 var AddGlobalNetRolesToAdmins = func(u *models.User) {}

+ 43 - 0
migrate/migrate.go

@@ -35,6 +35,7 @@ func Run() {
 	updateHosts()
 	updateNodes()
 	updateAcls()
+	updateNewAcls()
 	logic.MigrateToGws()
 	migrateToEgressV1()
 	resync()
@@ -441,6 +442,48 @@ func updateAcls() {
 	}
 }
 
+func updateNewAcls() {
+	if servercfg.IsPro {
+		userGroups, _ := logic.ListUserGroups()
+		userGroupMap := make(map[models.UserGroupID]models.UserGroup)
+		for _, userGroup := range userGroups {
+			userGroupMap[userGroup.ID] = userGroup
+		}
+
+		acls := logic.ListAcls()
+		for _, acl := range acls {
+			aclSrc := make([]models.AclPolicyTag, 0)
+			for _, src := range acl.Src {
+				if src.ID == models.UserGroupAclID {
+					userGroup, ok := userGroupMap[models.UserGroupID(src.Value)]
+					if !ok {
+						// if the group doesn't exist, don't add it to the acl's src.
+						continue
+					} else {
+						_, ok := userGroup.NetworkRoles[acl.NetworkID]
+						if !ok {
+							// if the group doesn't have permissions for the acl's
+							// network, don't add it to the acl's src.
+							continue
+						}
+					}
+				}
+				aclSrc = append(aclSrc, src)
+			}
+
+			if len(aclSrc) == 0 {
+				// if there are no acl sources, delete the acl.
+				_ = logic.DeleteAcl(acl)
+			} else if len(aclSrc) != len(acl.Src) {
+				// if some user groups were removed from the acl source,
+				// update the acl.
+				acl.Src = aclSrc
+				_ = logic.UpsertAcl(acl)
+			}
+		}
+	}
+}
+
 func MigrateEmqx() {
 
 	err := mq.SendPullSYN()

+ 128 - 6
pro/controllers/users.go

@@ -470,12 +470,14 @@ func createUserGroup(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	networks, err := logic.GetNetworks()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	for _, network := range networks {
+
+	for networkID := range userGroupReq.Group.NetworkRoles {
+		network, err := logic.GetNetwork(networkID.String())
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+
 		acl := models.Acl{
 			ID:          uuid.New().String(),
 			Name:        fmt.Sprintf("%s group", userGroupReq.Group.Name),
@@ -599,6 +601,93 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
 		},
 		Origin: models.Dashboard,
 	})
+
+	go func() {
+		networksAdded := make([]models.NetworkID, 0)
+		networksRemoved := make([]models.NetworkID, 0)
+
+		for networkID := range userGroup.NetworkRoles {
+			if _, ok := currUserG.NetworkRoles[networkID]; !ok {
+				networksAdded = append(networksAdded, networkID)
+			}
+		}
+
+		for networkID := range currUserG.NetworkRoles {
+			if _, ok := userGroup.NetworkRoles[networkID]; !ok {
+				networksRemoved = append(networksRemoved, networkID)
+			}
+		}
+
+		for _, networkID := range networksAdded {
+			// ensure the network exists.
+			network, err := logic.GetNetwork(networkID.String())
+			if err != nil {
+				continue
+			}
+
+			// insert acl if the network is added to the group.
+			acl := models.Acl{
+				ID:          uuid.New().String(),
+				Name:        fmt.Sprintf("%s group", userGroup.Name),
+				MetaData:    "This Policy allows user group to communicate with all gateways",
+				Default:     false,
+				ServiceType: models.Any,
+				NetworkID:   models.NetworkID(network.NetID),
+				Proto:       models.ALL,
+				RuleType:    models.UserPolicy,
+				Src: []models.AclPolicyTag{
+					{
+						ID:    models.UserGroupAclID,
+						Value: userGroup.ID.String(),
+					},
+				},
+				Dst: []models.AclPolicyTag{
+					{
+						ID:    models.NodeTagID,
+						Value: fmt.Sprintf("%s.%s", models.NetworkID(network.NetID), models.GwTagName),
+					}},
+				AllowedDirection: models.TrafficDirectionUni,
+				Enabled:          true,
+				CreatedBy:        "auto",
+				CreatedAt:        time.Now().UTC(),
+			}
+			_ = logic.InsertAcl(acl)
+		}
+
+		// since this group doesn't have a role for this network,
+		// there is no point in having this group as src in any
+		// of the network's acls.
+		for _, networkID := range networksRemoved {
+			acls, err := logic.ListAclsByNetwork(networkID)
+			if err != nil {
+				continue
+			}
+
+			for _, acl := range acls {
+				var hasGroupSrc bool
+				newAclSrc := make([]models.AclPolicyTag, 0)
+				for _, src := range acl.Src {
+					if src.ID == models.UserGroupAclID && src.Value == userGroup.ID.String() {
+						hasGroupSrc = true
+					} else {
+						newAclSrc = append(newAclSrc, src)
+					}
+				}
+
+				if hasGroupSrc {
+					if len(newAclSrc) == 0 {
+						// no other src exists, delete acl.
+						_ = logic.DeleteAcl(acl)
+					} else {
+						// other sources exist, update acl.
+						acl.Src = newAclSrc
+						_ = logic.UpsertAcl(acl)
+					}
+				}
+			}
+		}
+	}()
+
 	// reset configs for service user
 	go proLogic.UpdatesUserGwAccessOnGrpUpdates(currUserG.NetworkRoles, userGroup.NetworkRoles)
 	logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
@@ -658,6 +747,39 @@ func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
 		},
 		Origin: models.Dashboard,
 	})
+
+	go func() {
+		for networkID := range userG.NetworkRoles {
+			acls, err := logic.ListAclsByNetwork(networkID)
+			if err != nil {
+				continue
+			}
+
+			for _, acl := range acls {
+				var hasGroupSrc bool
+				newAclSrc := make([]models.AclPolicyTag, 0)
+				for _, src := range acl.Src {
+					if src.ID == models.UserGroupAclID && src.Value == userG.ID.String() {
+						hasGroupSrc = true
+					} else {
+						newAclSrc = append(newAclSrc, src)
+					}
+				}
+
+				if hasGroupSrc {
+					if len(newAclSrc) == 0 {
+						// no other src exists, delete acl.
+						_ = logic.DeleteAcl(acl)
+					} else {
+						// other sources exist, update acl.
+						acl.Src = newAclSrc
+						_ = logic.UpsertAcl(acl)
+					}
+				}
+			}
+		}
+	}()
+
 	go proLogic.UpdatesUserGwAccessOnGrpUpdates(userG.NetworkRoles, make(map[models.NetworkID]map[models.UserRoleID]struct{}))
 	logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user group")
 }

+ 1 - 0
pro/initialize.go

@@ -132,6 +132,7 @@ func InitPro() {
 	logic.MigrateToUUIDs = proLogic.MigrateToUUIDs
 	logic.IntialiseGroups = proLogic.UserGroupsInit
 	logic.AddGlobalNetRolesToAdmins = proLogic.AddGlobalNetRolesToAdmins
+	logic.ListUserGroups = proLogic.ListUserGroups
 	logic.GetUserGroupsInNetwork = proLogic.GetUserGroupsInNetwork
 	logic.GetUserGroup = proLogic.GetUserGroup
 	logic.GetNodeStatus = proLogic.GetNodeStatus