diff --git a/logic/acls.go b/logic/acls.go index 876f0d77..f62c3c35 100644 --- a/logic/acls.go +++ b/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 } diff --git a/logic/user_mgmt.go b/logic/user_mgmt.go index f518eb89..2d480480 100644 --- a/logic/user_mgmt.go +++ b/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) {} diff --git a/migrate/migrate.go b/migrate/migrate.go index d21052c7..f54742d5 100644 --- a/migrate/migrate.go +++ b/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() diff --git a/pro/controllers/users.go b/pro/controllers/users.go index 149c89fd..2ba43037 100644 --- a/pro/controllers/users.go +++ b/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") } diff --git a/pro/initialize.go b/pro/initialize.go index ae6788c4..6d8ea426 100644 --- a/pro/initialize.go +++ b/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