netmaker/logic/users.go
Vishal Dalwadi 3551e8e24e
NET-1996: Add Support for TOTP Authentication. (#3517)
* feat(git): ignore run configurations;

* feat(go): add support for TOTP authentication;

* fix(go): api docs;

* fix(go): static checks failing;

* fix(go): ignore mfa enforcement for user auth;

* feat(go): allow resetting mfa;

* feat(go): allow resetting mfa;

* feat(go): use library function;

* fix(go): signature;

* feat(go): allow only master user to unset user's mfa;

* feat(go): set caller when master to prevent panic;

* feat(go): make messages more user friendly;

* fix(go): run go mod tidy;

* fix(go): optimize imports;

* fix(go): return unauthorized on token expiry;

* fix(go): move mfa endpoints under username;

* fix(go): set is mfa enabled when converting;

* feat(go): allow authenticated users to use preauth apis;

* feat(go): set correct header value;

* feat(go): allow super-admins and admins to unset mfa;

* feat(go): allow user to unset mfa if not enforced;
2025-06-26 08:29:13 +05:30

214 lines
5.5 KiB
Go

package logic
import (
"encoding/json"
"errors"
"sort"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
)
// GetUser - gets a user
// TODO support "masteradmin"
func GetUser(username string) (*models.User, error) {
var user models.User
record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
if err != nil {
return &user, err
}
if err = json.Unmarshal([]byte(record), &user); err != nil {
return &models.User{}, err
}
return &user, err
}
// GetReturnUser - gets a user
func GetReturnUser(username string) (models.ReturnUser, error) {
var user models.ReturnUser
record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
if err != nil {
return user, err
}
if err = json.Unmarshal([]byte(record), &user); err != nil {
return models.ReturnUser{}, err
}
return user, err
}
// ToReturnUser - gets a user as a return user
func ToReturnUser(user models.User) models.ReturnUser {
return models.ReturnUser{
UserName: user.UserName,
ExternalIdentityProviderID: user.ExternalIdentityProviderID,
IsMFAEnabled: user.IsMFAEnabled,
DisplayName: user.DisplayName,
AccountDisabled: user.AccountDisabled,
AuthType: user.AuthType,
RemoteGwIDs: user.RemoteGwIDs,
UserGroups: user.UserGroups,
PlatformRoleID: user.PlatformRoleID,
NetworkRoles: user.NetworkRoles,
LastLoginTime: user.LastLoginTime,
}
}
// SetUserDefaults - sets the defaults of a user to avoid empty fields
func SetUserDefaults(user *models.User) {
if user.RemoteGwIDs == nil {
user.RemoteGwIDs = make(map[string]struct{})
}
if len(user.NetworkRoles) == 0 {
user.NetworkRoles = make(map[models.NetworkID]map[models.UserRoleID]struct{})
}
if len(user.UserGroups) == 0 {
user.UserGroups = make(map[models.UserGroupID]struct{})
}
}
// SortUsers - Sorts slice of Users by username
func SortUsers(unsortedUsers []models.ReturnUser) {
sort.Slice(unsortedUsers, func(i, j int) bool {
return unsortedUsers[i].UserName < unsortedUsers[j].UserName
})
}
// GetSuperAdmin - fetches superadmin user
func GetSuperAdmin() (models.ReturnUser, error) {
users, err := GetUsers()
if err != nil {
return models.ReturnUser{}, err
}
for _, user := range users {
if user.IsSuperAdmin || user.PlatformRoleID == models.SuperAdminRole {
return user, nil
}
}
return models.ReturnUser{}, errors.New("superadmin not found")
}
func InsertPendingUser(u *models.User) error {
data, err := json.Marshal(u)
if err != nil {
return err
}
return database.Insert(u.UserName, string(data), database.PENDING_USERS_TABLE_NAME)
}
func DeletePendingUser(username string) error {
return database.DeleteRecord(database.PENDING_USERS_TABLE_NAME, username)
}
func IsPendingUser(username string) bool {
records, err := database.FetchRecords(database.PENDING_USERS_TABLE_NAME)
if err != nil {
return false
}
for _, record := range records {
u := models.ReturnUser{}
err := json.Unmarshal([]byte(record), &u)
if err == nil && u.UserName == username {
return true
}
}
return false
}
func ListPendingReturnUsers() ([]models.ReturnUser, error) {
pendingUsers := []models.ReturnUser{}
records, err := database.FetchRecords(database.PENDING_USERS_TABLE_NAME)
if err != nil && !database.IsEmptyRecord(err) {
return pendingUsers, err
}
for _, record := range records {
u := models.ReturnUser{}
err = json.Unmarshal([]byte(record), &u)
if err == nil {
pendingUsers = append(pendingUsers, u)
}
}
return pendingUsers, nil
}
func ListPendingUsers() ([]models.User, error) {
var pendingUsers []models.User
records, err := database.FetchRecords(database.PENDING_USERS_TABLE_NAME)
if err != nil && !database.IsEmptyRecord(err) {
return pendingUsers, err
}
for _, record := range records {
var u models.User
err = json.Unmarshal([]byte(record), &u)
if err == nil {
pendingUsers = append(pendingUsers, u)
}
}
return pendingUsers, nil
}
func GetUserMap() (map[string]models.User, error) {
userMap := make(map[string]models.User)
records, err := database.FetchRecords(database.USERS_TABLE_NAME)
if err != nil && !database.IsEmptyRecord(err) {
return userMap, err
}
for _, record := range records {
u := models.User{}
err = json.Unmarshal([]byte(record), &u)
if err == nil {
userMap[u.UserName] = u
}
}
return userMap, nil
}
func InsertUserInvite(invite models.UserInvite) error {
data, err := json.Marshal(invite)
if err != nil {
return err
}
return database.Insert(invite.Email, string(data), database.USER_INVITES_TABLE_NAME)
}
func GetUserInvite(email string) (in models.UserInvite, err error) {
d, err := database.FetchRecord(database.USER_INVITES_TABLE_NAME, email)
if err != nil {
return
}
err = json.Unmarshal([]byte(d), &in)
return
}
func ListUserInvites() ([]models.UserInvite, error) {
invites := []models.UserInvite{}
records, err := database.FetchRecords(database.USER_INVITES_TABLE_NAME)
if err != nil && !database.IsEmptyRecord(err) {
return invites, err
}
for _, record := range records {
in := models.UserInvite{}
err = json.Unmarshal([]byte(record), &in)
if err == nil {
invites = append(invites, in)
}
}
return invites, nil
}
func DeleteUserInvite(email string) error {
return database.DeleteRecord(database.USER_INVITES_TABLE_NAME, email)
}
func ValidateAndApproveUserInvite(email, code string) error {
in, err := GetUserInvite(email)
if err != nil {
return err
}
if code != in.InviteCode {
return errors.New("invalid code")
}
return nil
}