Files
netmaker/pro/logic/user_mgmt.go
T
VishalDalwadi 5bac8e1b07 feat(go): add schema for user invites table;
1. Schema Definition for User Invites table.
2. Use the newer table everywhere.
3. Migration code for User Invites table;
2026-04-13 10:52:57 +05:30

1460 lines
40 KiB
Go

package logic
import (
"context"
"errors"
"fmt"
"time"
"github.com/google/uuid"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/schema"
"gorm.io/datatypes"
"gorm.io/gorm"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog"
)
var (
globalNetworksAdminGroupID = schema.UserGroupID(fmt.Sprintf("global-%s-grp", schema.NetworkAdmin))
globalNetworksUserGroupID = schema.UserGroupID(fmt.Sprintf("global-%s-grp", schema.NetworkUser))
globalNetworksAdminRoleID = schema.UserRoleID(fmt.Sprintf("global-%s", schema.NetworkAdmin))
globalNetworksUserRoleID = schema.UserRoleID(fmt.Sprintf("global-%s", schema.NetworkUser))
)
var ServiceUserPermissionTemplate = schema.UserRole{
ID: schema.ServiceUser,
Default: true,
FullAccess: false,
DenyDashboardAccess: true,
}
var PlatformUserUserPermissionTemplate = schema.UserRole{
ID: schema.PlatformUser,
Default: true,
FullAccess: false,
}
var AuditorUserPermissionTemplate = schema.UserRole{
ID: schema.Auditor,
Default: true,
DenyDashboardAccess: false,
FullAccess: false,
NetworkLevelAccess: datatypes.NewJSONType(schema.ResourceAccess{
schema.NetworkRsrc: {
schema.AllNetworkRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
}),
}
var NetworkAdminAllPermissionTemplate = schema.UserRole{
ID: globalNetworksAdminRoleID,
Name: "Network Admins",
MetaData: "can manage configuration of all networks",
Default: true,
FullAccess: true,
NetworkID: schema.AllNetworks,
}
var NetworkUserAllPermissionTemplate = schema.UserRole{
ID: globalNetworksUserRoleID,
Name: "Network Users",
MetaData: "Can connect to nodes in your networks via Netmaker Desktop App.",
Default: true,
FullAccess: false,
NetworkID: schema.AllNetworks,
NetworkLevelAccess: datatypes.NewJSONType(schema.ResourceAccess{
schema.HostRsrc: {
schema.AllHostRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.RemoteAccessGwRsrc: {
schema.AllRemoteAccessGwRsrcID: schema.RsrcPermissionScope{
Read: true,
VPNaccess: true,
},
},
schema.ExtClientsRsrc: {
schema.AllExtClientsRsrcID: schema.RsrcPermissionScope{
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
},
schema.DnsRsrc: {
schema.AllDnsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.AclRsrc: {
schema.AllAclsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.EgressGwRsrc: {
schema.AllEgressGwRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.InetGwRsrc: {
schema.AllInetGwRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.RelayRsrc: {
schema.AllRelayRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.TagRsrc: {
schema.AllTagsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.PostureCheckRsrc: {
schema.AllPostureCheckRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.NameserverRsrc: {
schema.AllNameserverRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.JitUserRsrc: {
schema.AllJitUserRsrcID: schema.RsrcPermissionScope{
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
},
}),
}
func UserRolesInit() {
_ = logic.SuperAdminPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = logic.AdminPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = ServiceUserPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = PlatformUserUserPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = AuditorUserPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = NetworkAdminAllPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = NetworkUserAllPermissionTemplate.Upsert(db.WithContext(context.TODO()))
}
func UserGroupsInit() {
// create default network groups
var NetworkGlobalAdminGroup = schema.UserGroup{
ID: globalNetworksAdminGroupID,
Default: true,
Name: "All Networks Admin Group",
MetaData: "can manage configuration of all networks",
NetworkRoles: datatypes.NewJSONType(schema.NetworkRoles{
schema.AllNetworks: {
globalNetworksAdminRoleID: {},
},
}),
}
var NetworkGlobalUserGroup = schema.UserGroup{
ID: globalNetworksUserGroupID,
Name: "All Networks User Group",
Default: true,
NetworkRoles: datatypes.NewJSONType(schema.NetworkRoles{
schema.AllNetworks: {
globalNetworksUserRoleID: {},
},
}),
MetaData: "Provides read-only dashboard access to platform users and allows connection to network nodes via the Netmaker Desktop App.",
}
_ = NetworkGlobalAdminGroup.Upsert(db.WithContext(context.TODO()))
_ = NetworkGlobalUserGroup.Upsert(db.WithContext(context.TODO()))
}
func CreateDefaultNetworkRolesAndGroups(netID schema.NetworkID) {
if netID.String() == "" {
return
}
var NetworkAdminPermissionTemplate = schema.UserRole{
ID: GetDefaultNetworkAdminRoleID(netID),
Name: fmt.Sprintf("%s Admin", netID),
MetaData: fmt.Sprintf("can manage your network `%s` configuration.", netID),
Default: true,
NetworkID: netID,
FullAccess: true,
NetworkLevelAccess: datatypes.NewJSONType(schema.ResourceAccess{}),
}
var NetworkUserPermissionTemplate = schema.UserRole{
ID: GetDefaultNetworkUserRoleID(netID),
Name: fmt.Sprintf("%s User", netID),
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Netmaker Desktop App.", netID),
Default: true,
FullAccess: false,
NetworkID: netID,
DenyDashboardAccess: false,
NetworkLevelAccess: datatypes.NewJSONType(schema.ResourceAccess{
schema.HostRsrc: {
schema.AllHostRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.RemoteAccessGwRsrc: {
schema.AllRemoteAccessGwRsrcID: schema.RsrcPermissionScope{
Read: true,
VPNaccess: true,
},
},
schema.ExtClientsRsrc: {
schema.AllExtClientsRsrcID: schema.RsrcPermissionScope{
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
},
schema.DnsRsrc: {
schema.AllDnsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.AclRsrc: {
schema.AllAclsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.EgressGwRsrc: {
schema.AllEgressGwRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.InetGwRsrc: {
schema.AllInetGwRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.RelayRsrc: {
schema.AllRelayRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.TagRsrc: {
schema.AllTagsRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.PostureCheckRsrc: {
schema.AllPostureCheckRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.NameserverRsrc: {
schema.AllNameserverRsrcID: schema.RsrcPermissionScope{
Read: true,
},
},
schema.JitUserRsrc: {
schema.AllJitUserRsrcID: schema.RsrcPermissionScope{
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
},
}),
}
_ = NetworkAdminPermissionTemplate.Upsert(db.WithContext(context.TODO()))
_ = NetworkUserPermissionTemplate.Upsert(db.WithContext(context.TODO()))
// create default network groups
var NetworkAdminGroup = schema.UserGroup{
ID: GetDefaultNetworkAdminGroupID(netID),
Name: fmt.Sprintf("%s Admin Group", netID),
Default: true,
NetworkRoles: datatypes.NewJSONType(schema.NetworkRoles{
netID: {
GetDefaultNetworkAdminRoleID(netID): {},
},
}),
MetaData: fmt.Sprintf("can manage your network `%s` configuration including adding and removing devices.", netID),
}
var NetworkUserGroup = schema.UserGroup{
ID: GetDefaultNetworkUserGroupID(netID),
Name: fmt.Sprintf("%s User Group", netID),
Default: true,
NetworkRoles: datatypes.NewJSONType(schema.NetworkRoles{
netID: {
GetDefaultNetworkUserRoleID(netID): {},
},
}),
MetaData: fmt.Sprintf("Can connect to nodes in your network `%s` via Netmaker Desktop App. Platform users will have read-only access to the the dashboard.", netID),
}
_ = NetworkAdminGroup.Upsert(db.WithContext(context.TODO()))
_ = NetworkUserGroup.Upsert(db.WithContext(context.TODO()))
}
func DeleteNetworkRoles(netID string) {
users, err := (&schema.User{}).ListAll(db.WithContext(context.TODO()))
if err != nil {
return
}
defaultAdminGrpID := GetDefaultNetworkAdminGroupID(schema.NetworkID(netID))
defaultUserGrpID := GetDefaultNetworkUserGroupID(schema.NetworkID(netID))
for _, user := range users {
var upsert bool
if _, ok := user.UserGroups.Data()[defaultUserGrpID]; ok {
delete(user.UserGroups.Data(), defaultUserGrpID)
upsert = true
}
if _, ok := user.UserGroups.Data()[defaultAdminGrpID]; ok {
delete(user.UserGroups.Data(), defaultAdminGrpID)
upsert = true
}
if upsert {
logic.UpsertUser(user)
}
}
_ = (&schema.UserGroup{
ID: defaultUserGrpID,
}).Delete(db.WithContext(context.TODO()))
_ = (&schema.UserGroup{
ID: defaultAdminGrpID,
}).Delete(db.WithContext(context.TODO()))
userGs, _ := (&schema.UserGroup{}).ListAll(db.WithContext(context.TODO()))
for _, userGI := range userGs {
if _, ok := userGI.NetworkRoles.Data()[schema.NetworkID(netID)]; ok {
delete(userGI.NetworkRoles.Data(), schema.NetworkID(netID))
UpdateUserGroup(userGI)
}
}
networkRoles := &schema.UserRole{
NetworkID: schema.NetworkID(netID),
}
_ = networkRoles.DeleteNetworkRoles(db.WithContext(context.TODO()))
}
func ValidateCreateRoleReq(userRole *schema.UserRole) error {
// check if role exists with this id
roleCheck := &schema.UserRole{ID: userRole.ID}
err := roleCheck.Get(db.WithContext(context.TODO()))
if err == nil {
return fmt.Errorf("role with id `%s` exists already", userRole.ID.String())
}
if len(userRole.NetworkLevelAccess.Data()) > 0 {
for rsrcType := range userRole.NetworkLevelAccess.Data() {
if _, ok := schema.RsrcTypeMap[rsrcType]; !ok {
return errors.New("invalid rsrc type " + rsrcType.String())
}
if rsrcType == schema.RemoteAccessGwRsrc {
userRsrcPermissions := userRole.NetworkLevelAccess.Data()[schema.RemoteAccessGwRsrc]
var vpnAccess bool
for _, scope := range userRsrcPermissions {
if scope.VPNaccess {
vpnAccess = true
break
}
}
if vpnAccess {
userRole.NetworkLevelAccess.Data()[schema.ExtClientsRsrc] = map[schema.RsrcID]schema.RsrcPermissionScope{
schema.AllExtClientsRsrcID: {
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
}
}
}
}
}
if userRole.NetworkID == "" {
return errors.New("only network roles are allowed to be created")
}
return nil
}
func ValidateUpdateRoleReq(userRole *schema.UserRole) error {
roleInDB := &schema.UserRole{ID: userRole.ID}
err := roleInDB.Get(db.WithContext(context.TODO()))
if err != nil {
return err
}
if roleInDB.NetworkID != userRole.NetworkID {
return errors.New("network id mismatch")
}
if roleInDB.Default {
return errors.New("cannot update default role")
}
if len(userRole.NetworkLevelAccess.Data()) > 0 {
for rsrcType := range userRole.NetworkLevelAccess.Data() {
if _, ok := schema.RsrcTypeMap[rsrcType]; !ok {
return errors.New("invalid rsrc type " + rsrcType.String())
}
if rsrcType == schema.RemoteAccessGwRsrc {
userRsrcPermissions := userRole.NetworkLevelAccess.Data()[schema.RemoteAccessGwRsrc]
var vpnAccess bool
for _, scope := range userRsrcPermissions {
if scope.VPNaccess {
vpnAccess = true
break
}
}
if vpnAccess {
userRole.NetworkLevelAccess.Data()[schema.ExtClientsRsrc] = map[schema.RsrcID]schema.RsrcPermissionScope{
schema.AllExtClientsRsrcID: {
Read: true,
Create: true,
Update: true,
Delete: true,
SelfOnly: true,
},
}
}
}
}
}
return nil
}
// CreateRole - inserts new role into DB
func CreateRole(role *schema.UserRole) error {
// default roles are currently created directly in the db.
// this check is only to prevent future errors.
if role.Default && role.ID == "" {
return errors.New("role id cannot be empty for default role")
}
if !role.Default {
role.ID = schema.UserRoleID(uuid.NewString())
}
// check if the role already exists
if role.Name == "" {
return errors.New("role name cannot be empty")
}
exists, err := role.Exists(db.WithContext(context.TODO()))
if err != nil {
return err
}
if exists {
return errors.New("role already exists")
}
return role.Create(db.WithContext(context.TODO()))
}
// DeleteRole - deletes user role
func DeleteRole(rid schema.UserRoleID, force bool) error {
if rid.String() == "" {
return errors.New("role id cannot be empty")
}
users, err := (&schema.User{}).ListAll(db.WithContext(context.TODO()))
if err != nil {
return err
}
role := &schema.UserRole{ID: rid}
err = role.Get(db.WithContext(context.TODO()))
if err != nil {
return err
}
if role.NetworkID == "" {
return errors.New("cannot delete platform role")
}
// allow deletion of default network roles if network doesn't exist
if role.NetworkID == schema.AllNetworks {
return errors.New("cannot delete default network role")
}
// check if network exists
exists, _ := logic.NetworkExists(role.NetworkID.String())
if role.Default {
if exists && !force {
return errors.New("cannot delete default role")
}
}
for _, user := range users {
for userG := range user.UserGroups.Data() {
ug, err := GetUserGroup(userG)
if err == nil {
if role.NetworkID != "" {
for netID, networkRoles := range ug.NetworkRoles.Data() {
if _, ok := networkRoles[rid]; ok {
delete(networkRoles, rid)
ug.NetworkRoles.Data()[netID] = networkRoles
UpdateUserGroup(ug)
}
}
}
}
}
if user.PlatformRoleID == rid {
err = errors.New("active roles cannot be deleted.switch existing users to a new role before deleting")
return err
}
}
return (&schema.UserRole{
ID: rid,
}).Delete(db.WithContext(context.TODO()))
}
func ValidateCreateGroupReq(g schema.UserGroup) error {
// check if network roles are valid
for _, roleMap := range g.NetworkRoles.Data() {
for roleID := range roleMap {
role := &schema.UserRole{ID: roleID}
err := role.Get(db.WithContext(context.TODO()))
if err != nil {
return fmt.Errorf("invalid network role %s", roleID)
}
if role.NetworkID == "" {
return errors.New("platform role cannot be used as network role")
}
}
}
return nil
}
func ValidateUpdateGroupReq(new schema.UserGroup) error {
var newHasAllNetworkRole, newHasSpecNetworkRole bool
for networkID := range new.NetworkRoles.Data() {
if networkID == schema.AllNetworks {
newHasAllNetworkRole = true
} else {
newHasSpecNetworkRole = true
}
userRolesMap := new.NetworkRoles.Data()[networkID]
for roleID := range userRolesMap {
netRole := &schema.UserRole{ID: roleID}
err := netRole.Get(db.WithContext(context.TODO()))
if err != nil {
err = fmt.Errorf("invalid network role")
return err
}
if netRole.NetworkID == "" {
return errors.New("platform role cannot be used as network role")
}
}
}
if newHasAllNetworkRole && newHasSpecNetworkRole {
return errors.New("cannot have networks roles for all networks and a specific network")
}
return nil
}
// CreateUserGroup - creates new user group
func CreateUserGroup(g *schema.UserGroup) error {
// default groups are currently created directly in the db.
// this check is only to prevent future errors.
if g.Default && g.ID == "" {
return errors.New("group id cannot be empty for default group")
}
if !g.Default {
g.ID = schema.UserGroupID(uuid.NewString())
}
// check if the group already exists
if g.Name == "" {
return errors.New("group name cannot be empty")
}
err := (&schema.UserGroup{
Name: g.Name,
}).GetByName(db.WithContext(context.TODO()))
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
} else {
return errors.New("group already exists")
}
err = g.Create(db.WithContext(context.TODO()))
if err != nil {
return err
}
// create default network gateway policies
_ = EnsureDefaultUserGroupNetworkPolicies(nil, g)
return nil
}
// GetUserGroup - fetches user group
func GetUserGroup(gid schema.UserGroupID) (schema.UserGroup, error) {
group := schema.UserGroup{
ID: gid,
}
err := group.Get(db.WithContext(context.TODO()))
return group, err
}
func GetDefaultGlobalAdminGroupID() schema.UserGroupID {
return globalNetworksAdminGroupID
}
func GetDefaultGlobalUserGroupID() schema.UserGroupID {
return globalNetworksUserGroupID
}
func GetDefaultGlobalAdminRoleID() schema.UserRoleID {
return globalNetworksAdminRoleID
}
func GetDefaultGlobalUserRoleID() schema.UserRoleID {
return globalNetworksUserRoleID
}
func GetDefaultNetworkAdminGroupID(networkID schema.NetworkID) schema.UserGroupID {
return schema.UserGroupID(fmt.Sprintf("%s-%s-grp", networkID, schema.NetworkAdmin))
}
func GetDefaultNetworkUserGroupID(networkID schema.NetworkID) schema.UserGroupID {
return schema.UserGroupID(fmt.Sprintf("%s-%s-grp", networkID, schema.NetworkUser))
}
func GetDefaultNetworkAdminRoleID(networkID schema.NetworkID) schema.UserRoleID {
return schema.UserRoleID(fmt.Sprintf("%s-%s", networkID, schema.NetworkAdmin))
}
func GetDefaultNetworkUserRoleID(networkID schema.NetworkID) schema.UserRoleID {
return schema.UserRoleID(fmt.Sprintf("%s-%s", networkID, schema.NetworkUser))
}
func GetDefaultGroupAclName(groupName string) string {
return fmt.Sprintf("%s group", groupName)
}
// UpdateUserGroup - updates new user group
func UpdateUserGroup(g schema.UserGroup) error {
// check if the group exists
if g.ID == "" {
return errors.New("group id cannot be empty")
}
return g.Update(db.WithContext(context.TODO()))
}
func DeleteAndCleanUpGroup(group *schema.UserGroup) error {
// TODO: wrap in transaction once acls are migrated to sql schema.
users, err := (&schema.User{}).ListAll(db.WithContext(context.TODO()))
if err != nil {
return err
}
for _, user := range users {
delete(user.UserGroups.Data(), group.ID)
err = user.Update(db.WithContext(context.TODO()))
if err != nil {
return err
}
}
err = EnsureDefaultUserGroupNetworkPolicies(group, nil)
if err != nil {
return err
}
err = group.Delete(db.WithContext(context.TODO()))
if err != nil {
return err
}
go UpdatesUserGwAccessOnGrpUpdates(group.ID, group.NetworkRoles.Data(), make(map[schema.NetworkID]map[schema.UserRoleID]struct{}))
networksMap, err := GetGroupNetworksMap(group)
if err != nil {
return err
}
var replacePeers bool
if len(networksMap) > 0 {
replacePeers = true
}
go mq.PublishPeerUpdate(replacePeers)
for networkID := range networksMap {
go RemoveUserGroupFromPostureChecks(group.ID, networkID)
}
return nil
}
func GetUserRAGNodes(user *schema.User) (gws map[string]models.Node) {
gws = make(map[string]models.Node)
nodes, err := logic.GetAllNodes()
if err != nil {
return
}
for _, node := range nodes {
if !node.IsGw {
continue
}
if user.PlatformRoleID == schema.AdminRole || user.PlatformRoleID == schema.SuperAdminRole {
if ok, _ := IsUserAllowedToCommunicate(user.Username, node); ok {
gws[node.ID.String()] = node
continue
}
} else {
for groupID := range user.UserGroups.Data() {
userGrp, err := logic.GetUserGroup(groupID)
if err == nil {
if roles, ok := userGrp.NetworkRoles.Data()[schema.NetworkID(node.Network)]; ok && len(roles) > 0 {
if ok, _ := IsUserAllowedToCommunicate(user.Username, node); ok {
gws[node.ID.String()] = node
break
}
}
if roles, ok := userGrp.NetworkRoles.Data()[schema.AllNetworks]; ok && len(roles) > 0 {
if ok, _ := IsUserAllowedToCommunicate(user.Username, node); ok {
gws[node.ID.String()] = node
break
}
}
}
}
}
}
return
}
func GetFilteredNodesByUserAccess(user *schema.User, nodes []models.Node) (filteredNodes []models.Node) {
return nodes
}
func FilterNetworksByRole(allnetworks []schema.Network, user *schema.User) []schema.Network {
platformRole := &schema.UserRole{ID: user.PlatformRoleID}
err := platformRole.Get(db.WithContext(context.TODO()))
if err != nil {
return []schema.Network{}
}
if !platformRole.FullAccess {
allNetworkRoles := make(map[schema.NetworkID]struct{})
_, ok := platformRole.NetworkLevelAccess.Data()[schema.NetworkRsrc]
if ok {
perm, ok := platformRole.NetworkLevelAccess.Data()[schema.NetworkRsrc][schema.AllNetworkRsrcID]
if ok && perm.Read {
return allnetworks
}
}
for userGID := range user.UserGroups.Data() {
userG, err := GetUserGroup(userGID)
if err == nil {
if len(userG.NetworkRoles.Data()) > 0 {
for netID := range userG.NetworkRoles.Data() {
if netID == schema.AllNetworks {
return allnetworks
}
allNetworkRoles[netID] = struct{}{}
}
}
}
}
var filteredNetworks []schema.Network
for _, networkI := range allnetworks {
if _, ok := allNetworkRoles[schema.NetworkID(networkI.Name)]; ok {
filteredNetworks = append(filteredNetworks, networkI)
}
}
allnetworks = filteredNetworks
}
return allnetworks
}
func IsGroupsValid(groups map[schema.UserGroupID]struct{}) error {
for groupID := range groups {
_, err := GetUserGroup(groupID)
if err != nil {
return fmt.Errorf("user group `%s` not found", groupID)
}
}
return nil
}
func IsGroupValid(groupID schema.UserGroupID) error {
_, err := GetUserGroup(groupID)
if err != nil {
return fmt.Errorf("user group `%s` not found", groupID)
}
return nil
}
func IsNetworkRolesValid(networkRoles map[schema.NetworkID]map[schema.UserRoleID]struct{}) error {
for netID, netRoles := range networkRoles {
if netID != schema.AllNetworks {
err := (&schema.Network{Name: netID.String()}).Get(db.WithContext(context.TODO()))
if err != nil {
return fmt.Errorf("failed to fetch network %s ", netID)
}
}
for netRoleID := range netRoles {
role := &schema.UserRole{ID: netRoleID}
err := role.Get(db.WithContext(context.TODO()))
if err != nil {
return fmt.Errorf("failed to fetch role %s ", netRoleID)
}
if role.NetworkID == "" {
return fmt.Errorf("cannot use platform as network role %s", netRoleID)
}
}
}
return nil
}
// PrepareOauthUserFromInvite - init oauth user before create
func PrepareOauthUserFromInvite(in *schema.UserInvite) (schema.User, error) {
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
return schema.User{}, fetchErr
}
user := schema.User{
Username: in.Email,
Password: newPass,
}
user.UserGroups = in.UserGroups
user.PlatformRoleID = schema.UserRoleID(in.PlatformRoleID)
if user.PlatformRoleID == "" {
user.PlatformRoleID = schema.ServiceUser
}
return user, nil
}
func UpdatesUserGwAccessOnRoleUpdates(currNetworkAccess,
changeNetworkAccess map[schema.RsrcType]map[schema.RsrcID]schema.RsrcPermissionScope, netID string) {
networkChangeMap := make(map[schema.RsrcID]schema.RsrcPermissionScope)
for rsrcType, RsrcPermsMap := range currNetworkAccess {
if rsrcType != schema.RemoteAccessGwRsrc {
continue
}
if _, ok := changeNetworkAccess[rsrcType]; !ok {
for rsrcID, scope := range RsrcPermsMap {
networkChangeMap[rsrcID] = scope
}
} else {
for rsrcID, scope := range RsrcPermsMap {
if _, ok := changeNetworkAccess[rsrcType][rsrcID]; !ok {
networkChangeMap[rsrcID] = scope
}
}
}
}
extclients, err := logic.GetAllExtClients()
if err != nil {
slog.Error("failed to fetch extclients", "error", err)
return
}
userMap, err := logic.GetUserMap()
if err != nil {
return
}
for _, extclient := range extclients {
if extclient.Network != netID {
continue
}
if _, ok := networkChangeMap[schema.AllRemoteAccessGwRsrcID]; ok {
if user, ok := userMap[extclient.OwnerID]; ok {
if user.PlatformRoleID != schema.ServiceUser {
continue
}
err = logic.DeleteExtClientAndCleanup(extclient)
if err != nil {
slog.Error("failed to delete extclient",
"id", extclient.ClientID, "owner", user.Username, "error", err)
} else {
if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
slog.Error("error setting ext peers: " + err.Error())
}
}
}
continue
}
if _, ok := networkChangeMap[schema.RsrcID(extclient.IngressGatewayID)]; ok {
if user, ok := userMap[extclient.OwnerID]; ok {
if user.PlatformRoleID != schema.ServiceUser {
continue
}
err = logic.DeleteExtClientAndCleanup(extclient)
if err != nil {
slog.Error("failed to delete extclient",
"id", extclient.ClientID, "owner", user.Username, "error", err)
} else {
if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
slog.Error("error setting ext peers: " + err.Error())
}
}
}
}
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}
func UpdatesUserGwAccessOnGrpUpdates(groupID schema.UserGroupID, oldNetworkRoles, newNetworkRoles map[schema.NetworkID]map[schema.UserRoleID]struct{}) {
networkRemovedMap := make(map[schema.NetworkID]struct{})
for netID := range oldNetworkRoles {
if _, ok := newNetworkRoles[netID]; !ok {
networkRemovedMap[netID] = struct{}{}
}
}
extclients, err := logic.GetAllExtClients()
if err != nil {
slog.Error("failed to fetch extclients", "error", err)
return
}
userMap, err := logic.GetUserMap()
if err != nil {
return
}
for _, extclient := range extclients {
var shouldDelete bool
user, ok := userMap[extclient.OwnerID]
if !ok {
// user does not exist, delete extclient.
shouldDelete = true
} else {
if user.PlatformRoleID == schema.SuperAdminRole || user.PlatformRoleID == schema.AdminRole {
// Super-admin and Admin's access is not determined by group membership
// or network roles. Even if a network is removed from the group, they
// continue to have access to the network.
// So, no need to delete the extclient.
shouldDelete = false
} else {
_, userInGroup := user.UserGroups.Data()[groupID]
_, networkRemoved := networkRemovedMap[schema.NetworkID(extclient.Network)]
_, allNetworkAccessRemoved := networkRemovedMap[schema.AllNetworks]
if userInGroup && (networkRemoved || allNetworkAccessRemoved) {
// This group no longer provides it's members access to the
// network.
// This user is a member of the group and has no direct
// access to the network (either by its platform role or by
// network roles).
// This user is a member of the group and access to this
// network was previously given through the all network
// role and is now removed.
// So, delete the extclient.
shouldDelete = true
}
}
}
if shouldDelete {
err = logic.DeleteExtClientAndCleanup(extclient)
if err != nil {
slog.Error("failed to delete extclient",
"id", extclient.ClientID, "owner", user.Username, "error", err)
} else {
if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
slog.Error("error setting ext peers: " + err.Error())
}
}
}
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}
func UpdateUserGwAccess(currentUser, changeUser *schema.User) {
if changeUser.PlatformRoleID != schema.ServiceUser {
return
}
networkChangeMap := make(map[schema.NetworkID]map[schema.UserRoleID]struct{})
for gID := range currentUser.UserGroups.Data() {
if _, ok := changeUser.UserGroups.Data()[gID]; ok {
continue
}
userG, err := GetUserGroup(gID)
if err == nil {
for netID, networkUserRoles := range userG.NetworkRoles.Data() {
for netRoleID := range networkUserRoles {
if _, ok := networkChangeMap[netID]; !ok {
networkChangeMap[netID] = make(map[schema.UserRoleID]struct{})
}
networkChangeMap[netID][netRoleID] = struct{}{}
}
}
}
}
if len(networkChangeMap) == 0 {
return
}
// TODO - cleanup gw access when role and groups are updated
//removedGwAccess
extclients, err := logic.GetAllExtClients()
if err != nil {
slog.Error("failed to fetch extclients", "error", err)
return
}
for _, extclient := range extclients {
if extclient.OwnerID == currentUser.Username {
if _, ok := networkChangeMap[schema.NetworkID(extclient.Network)]; ok {
err = logic.DeleteExtClientAndCleanup(extclient)
if err != nil {
slog.Error("failed to delete extclient",
"id", extclient.ClientID, "owner", changeUser.Username, "error", err)
} else {
if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
slog.Error("error setting ext peers: " + err.Error())
}
}
}
}
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}
func EnsureDefaultUserGroupNetworkPolicies(old, new *schema.UserGroup) error {
oldNetworks, err := GetGroupNetworksMap(old)
if err != nil {
return err
}
newNetworks, err := GetGroupNetworksMap(new)
if err != nil {
return err
}
networksRemoved := make(map[schema.NetworkID]schema.Network)
for networkID, network := range oldNetworks {
_, ok := newNetworks[networkID]
if !ok {
networksRemoved[networkID] = network
}
}
networksAdded := make(map[schema.NetworkID]schema.Network)
for networkID, network := range newNetworks {
_, ok := oldNetworks[networkID]
if !ok {
networksAdded[networkID] = network
}
}
var groupID, groupName string
if old == nil {
if new == nil {
return fmt.Errorf("old and new cannot both be nil")
}
groupID = new.ID.String()
groupName = new.Name
} else {
groupID = old.ID.String()
groupName = old.Name
}
// For each network added, we add an ACL policy for this group to access this network.
defaultAclName := GetDefaultGroupAclName(groupName)
for networkID, network := range networksAdded {
var exists bool
acls, err := logic.ListAclsByNetwork(networkID)
if err != nil {
continue
}
for _, acl := range acls {
if acl.Name == defaultAclName && acl.Default {
exists = true
break
}
}
if !exists {
_ = logic.InsertAcl(models.Acl{
ID: uuid.New().String(),
Name: defaultAclName,
MetaData: "This Policy allows user group to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: schema.NetworkID(network.Name),
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: groupID,
},
},
Dst: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: fmt.Sprintf("%s.%s", schema.NetworkID(network.Name), models.GwTagName),
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
})
}
}
// For each network removed, remove the group as the src from all the ACLs.
for networkID := range networksRemoved {
acls, err := logic.ListAclsByNetwork(networkID)
if err != nil {
continue
}
for _, acl := range acls {
// This commented if condition is a special case for the code that follows it.
// The default ACL for this group, will only have this group as the src.
// The code that follows it, removes this group as the src from the ACL and
// constructs the new src slice.
// Since, there are no other srcs in the default group ACL, it will be empty
// and hence, the ACL will be deleted, which is what this if condition also
// does.
//if acl.Default && acl.Name == defaultAclName {
// _ = logic.DeleteAcl(acl)
//}
var newAclSrc []models.AclPolicyTag
var groupSrcExists bool
for _, src := range acl.Src {
if src.ID == models.UserGroupAclID && src.Value == groupID {
groupSrcExists = true
} else {
newAclSrc = append(newAclSrc, src)
}
}
if groupSrcExists {
if len(newAclSrc) == 0 {
_ = logic.DeleteAcl(acl)
} else {
acl.Src = newAclSrc
_ = logic.UpsertAcl(acl)
}
}
}
}
return nil
}
func GetGroupNetworksMap(g *schema.UserGroup) (map[schema.NetworkID]schema.Network, error) {
networksMap := make(map[schema.NetworkID]schema.Network)
if g == nil {
return networksMap, nil
}
if _, ok := g.NetworkRoles.Data()[schema.AllNetworks]; ok {
var err error
networks, err := (&schema.Network{}).ListAll(db.WithContext(context.TODO()))
if err != nil {
return nil, err
}
for _, network := range networks {
networksMap[schema.NetworkID(network.Name)] = network
}
} else {
for networkID := range g.NetworkRoles.Data() {
network := &schema.Network{Name: networkID.String()}
err := network.Get(db.WithContext(context.TODO()))
if err != nil {
return nil, err
}
networksMap[schema.NetworkID(network.Name)] = *network
}
}
return networksMap, nil
}
func CreateDefaultUserPolicies(netID schema.NetworkID) {
if netID.String() == "" {
return
}
if !logic.IsAclExists(fmt.Sprintf("%s.%s", netID, "all-users")) {
defaultUserAcl := models.Acl{
ID: fmt.Sprintf("%s.%s", netID, "all-users"),
Default: true,
Name: "All Users",
MetaData: "This policy gives access to everything in the network for an user",
NetworkID: netID,
Proto: models.ALL,
ServiceType: models.Any,
Port: []string{},
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserAclID,
Value: "*",
},
},
Dst: []models.AclPolicyTag{{
ID: models.NodeTagID,
Value: "*",
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(defaultUserAcl)
}
if !logic.IsAclExists(fmt.Sprintf("%s.%s-grp", netID, schema.NetworkAdmin)) {
networkAdminGroupID := GetDefaultNetworkAdminGroupID(netID)
defaultUserAcl := models.Acl{
ID: fmt.Sprintf("%s.%s-grp", netID, schema.NetworkAdmin),
Name: "Network Admin",
MetaData: "This Policy allows all network admins to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: netID,
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: globalNetworksAdminGroupID.String(),
},
{
ID: models.UserGroupAclID,
Value: networkAdminGroupID.String(),
},
},
Dst: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: fmt.Sprintf("%s.%s", netID, models.GwTagName),
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(defaultUserAcl)
}
if !logic.IsAclExists(fmt.Sprintf("%s.%s-grp", netID, schema.NetworkUser)) {
networkUserGroupID := GetDefaultNetworkUserGroupID(netID)
defaultUserAcl := models.Acl{
ID: fmt.Sprintf("%s.%s-grp", netID, schema.NetworkUser),
Name: "Network User",
MetaData: "This Policy allows all network users to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: netID,
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: globalNetworksUserGroupID.String(),
},
{
ID: models.UserGroupAclID,
Value: networkUserGroupID.String(),
},
},
Dst: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: fmt.Sprintf("%s.%s", netID, models.GwTagName),
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
}
logic.InsertAcl(defaultUserAcl)
}
groups, _ := (&schema.UserGroup{}).ListAll(db.WithContext(context.TODO()))
for _, group := range groups {
if group.Default {
continue
}
var hasAccess bool
if _, ok := group.NetworkRoles.Data()[schema.AllNetworks]; ok {
hasAccess = true
}
if _, ok := group.NetworkRoles.Data()[netID]; ok {
hasAccess = true
}
if hasAccess {
var exists bool
acls, err := logic.ListAclsByNetwork(netID)
if err != nil {
continue
}
defaultAclName := GetDefaultGroupAclName(group.Name)
for _, acl := range acls {
if acl.Name == defaultAclName && acl.Default {
exists = true
break
}
}
if !exists {
_ = logic.InsertAcl(models.Acl{
ID: uuid.New().String(),
Name: defaultAclName,
MetaData: "This Policy allows user group to communicate with all gateways",
Default: true,
ServiceType: models.Any,
NetworkID: netID,
Proto: models.ALL,
RuleType: models.UserPolicy,
Src: []models.AclPolicyTag{
{
ID: models.UserGroupAclID,
Value: group.ID.String(),
},
},
Dst: []models.AclPolicyTag{
{
ID: models.NodeTagID,
Value: fmt.Sprintf("%s.%s", netID, models.GwTagName),
}},
AllowedDirection: models.TrafficDirectionUni,
Enabled: true,
CreatedBy: "auto",
CreatedAt: time.Now().UTC(),
})
}
}
}
}
func GetUserGroupsInNetwork(netID schema.NetworkID) (networkGrps map[schema.UserGroupID]schema.UserGroup) {
groups, _ := (&schema.UserGroup{}).ListAll(db.WithContext(context.TODO()))
networkGrps = make(map[schema.UserGroupID]schema.UserGroup)
for _, grp := range groups {
if _, ok := grp.NetworkRoles.Data()[schema.AllNetworks]; ok {
networkGrps[grp.ID] = grp
continue
}
if _, ok := grp.NetworkRoles.Data()[netID]; ok {
networkGrps[grp.ID] = grp
}
}
return
}
func AddGlobalNetRolesToAdmins(u *schema.User) {
if u.PlatformRoleID != schema.SuperAdminRole && u.PlatformRoleID != schema.AdminRole {
return
}
if len(u.UserGroups.Data()) == 0 {
u.UserGroups = datatypes.NewJSONType(make(map[schema.UserGroupID]struct{}))
}
u.UserGroups.Data()[globalNetworksAdminGroupID] = struct{}{}
}
func GetUserGrpMap() map[schema.UserGroupID]map[string]struct{} {
grpUsersMap := make(map[schema.UserGroupID]map[string]struct{})
users, _ := (&schema.User{}).ListAll(db.WithContext(context.TODO()))
for _, user := range users {
for gID := range user.UserGroups.Data() {
if grpUsers, ok := grpUsersMap[gID]; ok {
grpUsers[user.Username] = struct{}{}
grpUsersMap[gID] = grpUsers
} else {
grpUsersMap[gID] = make(map[string]struct{})
grpUsersMap[gID][user.Username] = struct{}{}
}
}
}
return grpUsersMap
}
// IsNetworkAdmin - checks if user is a network admin via user groups
func IsNetworkAdmin(user *schema.User, networkID string) bool {
networkIDModel := schema.NetworkID(networkID)
allNetworksID := schema.AllNetworks
// Check platform role
if user.PlatformRoleID == schema.SuperAdminRole || user.PlatformRoleID == schema.AdminRole {
return true
}
// Check user groups for network admin roles
for groupID := range user.UserGroups.Data() {
group, err := logic.GetUserGroup(groupID)
if err != nil {
continue
}
if groupID == globalNetworksAdminGroupID {
return true
}
// Check if group has network admin role for this network
if roles, ok := group.NetworkRoles.Data()[networkIDModel]; ok {
for roleID := range roles {
if roleID == GetDefaultNetworkAdminRoleID(networkIDModel) {
return true
}
}
}
// Check if group has global network admin role
if roles, ok := group.NetworkRoles.Data()[allNetworksID]; ok {
for roleID := range roles {
if roleID == GetDefaultNetworkAdminRoleID(networkIDModel) || roleID == globalNetworksAdminRoleID {
return true
}
}
}
}
return false
}