From caf5b38de6f0714adb62e5292600f3931c02638d Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 18 Mar 2026 21:27:53 +0530 Subject: [PATCH 1/4] feat(go): match azure user by id; --- pro/auth/azure-ad.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/pro/auth/azure-ad.go b/pro/auth/azure-ad.go index 428e3fa6..338781c6 100644 --- a/pro/auth/azure-ad.go +++ b/pro/auth/azure-ad.go @@ -90,7 +90,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) { return } - user, err := logic.GetUser(content.UserPrincipalName) + user, err := GetMatchingUser(content) if err != nil { if database.IsEmptyRecord(err) { // user must not exist, so try to make one if inviteExists { @@ -140,7 +140,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) { } } - user, err = logic.GetUser(content.UserPrincipalName) + user, err = GetMatchingUser(content) if err != nil { handleOauthUserNotFound(w) return @@ -195,6 +195,30 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, servercfg.GetFrontendURL()+"/login?login="+jwt+"&user="+user.UserName, http.StatusPermanentRedirect) } +func GetMatchingUser(oauthUser *OAuthUser) (*models.User, error) { + user, err := logic.GetUser(oauthUser.UserPrincipalName) + if err != nil { + if !database.IsEmptyRecord(err) { + return nil, err + } + } else { + return user, nil + } + + users, err := logic.GetUsersDB() + if err != nil { + return nil, err + } + + for _, user := range users { + if user.ExternalIdentityProviderID == string(oauthUser.ID) { + return &user, nil + } + } + + return nil, errors.New(database.NO_RECORD) +} + func getAzureUserInfo(state string, code string) (*OAuthUser, error) { oauth_state_string, isValid := logic.IsStateValid(state) if (!isValid || state != oauth_state_string) && !isStateCached(state) { From 32ca51db92f77319c35b83846c83ebed836ada75 Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 8 Apr 2026 16:19:18 +0530 Subject: [PATCH 2/4] fix(go): set max open connections to 1; --- db/sqlite.go | 2 +- migrate/migrate_v1_5_1.go | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/db/sqlite.go b/db/sqlite.go index 356c6203..6e3df11f 100644 --- a/db/sqlite.go +++ b/db/sqlite.go @@ -62,7 +62,7 @@ func (s *sqliteConnector) connect() (*gorm.DB, error) { return nil, err } - //sqlDB.SetMaxOpenConns(1) + sqlDB.SetMaxOpenConns(1) sqlDB.SetMaxIdleConns(1) return db, nil diff --git a/migrate/migrate_v1_5_1.go b/migrate/migrate_v1_5_1.go index 7044241e..297f08f6 100644 --- a/migrate/migrate_v1_5_1.go +++ b/migrate/migrate_v1_5_1.go @@ -93,7 +93,7 @@ func migrateV1_5_1(ctx context.Context) error { } func migrateUsers(ctx context.Context) error { - records, err := database.FetchRecords(database.USERS_TABLE_NAME) + records, err := FetchAll(ctx, database.USERS_TABLE_NAME) if err != nil && !database.IsEmptyRecord(err) { return err } @@ -147,7 +147,7 @@ func migrateUsers(ctx context.Context) error { } func migrateNetworks(ctx context.Context) error { - records, err := database.FetchRecords(database.NETWORKS_TABLE_NAME) + records, err := FetchAll(ctx, database.NETWORKS_TABLE_NAME) if err != nil && !database.IsEmptyRecord(err) { return err } @@ -286,7 +286,7 @@ func migrateNetworks(ctx context.Context) error { } func migrateUserRoles(ctx context.Context) error { - records, err := database.FetchRecords(database.USER_PERMISSIONS_TABLE_NAME) + records, err := FetchAll(ctx, database.USER_PERMISSIONS_TABLE_NAME) if err != nil && !database.IsEmptyRecord(err) { return err } @@ -311,7 +311,7 @@ func migrateUserRoles(ctx context.Context) error { } func migrateUserGroups(ctx context.Context) error { - records, err := database.FetchRecords(database.USER_GROUPS_TABLE_NAME) + records, err := FetchAll(ctx, database.USER_GROUPS_TABLE_NAME) if err != nil && !database.IsEmptyRecord(err) { return err } @@ -336,7 +336,7 @@ func migrateUserGroups(ctx context.Context) error { } func migrateHosts(ctx context.Context) error { - records, err := database.FetchRecords(database.HOSTS_TABLE_NAME) + records, err := FetchAll(ctx, database.HOSTS_TABLE_NAME) if err != nil && !database.IsEmptyRecord(err) { return err } @@ -423,3 +423,22 @@ func migrateHosts(ctx context.Context) error { return nil } + +func FetchAll(ctx context.Context, tableName string) (map[string]string, error) { + row, err := db.FromContext(ctx).Raw("SELECT * FROM " + tableName + " ORDER BY key").Rows() + if err != nil { + return nil, err + } + records := make(map[string]string) + defer row.Close() + for row.Next() { // Iterate and fetch the records from result cursor + var key string + var value string + row.Scan(&key, &value) + records[key] = value + } + if len(records) == 0 { + return nil, gorm.ErrRecordNotFound + } + return records, nil +} From 109dab9055e334712ebdaf7c8e1b85647cf4fb0c Mon Sep 17 00:00:00 2001 From: abhishek9686 Date: Wed, 8 Apr 2026 19:05:49 +0530 Subject: [PATCH 3/4] v1.5.1: add mutex for sqlite write ops --- database/sqlite.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/database/sqlite.go b/database/sqlite.go index 2b87c6d8..513c9414 100644 --- a/database/sqlite.go +++ b/database/sqlite.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "errors" + "sync" "time" "github.com/gravitl/netmaker/db" @@ -13,6 +14,9 @@ import ( // SqliteDB is the db object for sqlite database connections var SqliteDB *sql.DB +// sqliteWriteMu serializes SQLite write operations to reduce lock contention. +var sqliteWriteMu sync.Mutex + // SQLITE_FUNCTIONS - contains a map of the functions for sqlite var SQLITE_FUNCTIONS = map[string]interface{}{ INIT_DB: initSqliteDB, @@ -40,6 +44,9 @@ func initSqliteDB() error { } func sqliteCreateTable(tableName string) error { + sqliteWriteMu.Lock() + defer sqliteWriteMu.Unlock() + statement, err := SqliteDB.Prepare("CREATE TABLE IF NOT EXISTS " + tableName + " (key TEXT NOT NULL UNIQUE PRIMARY KEY, value TEXT)") if err != nil { return err @@ -54,6 +61,9 @@ func sqliteCreateTable(tableName string) error { func sqliteInsert(key string, value string, tableName string) error { if key != "" && value != "" { + sqliteWriteMu.Lock() + defer sqliteWriteMu.Unlock() + insertSQL := "INSERT OR REPLACE INTO " + tableName + " (key, value) VALUES (?, ?)" statement, err := SqliteDB.Prepare(insertSQL) if err != nil { @@ -81,6 +91,9 @@ func sqliteInsertPeer(key string, value string) error { } func sqliteDeleteRecord(tableName string, key string) error { + sqliteWriteMu.Lock() + defer sqliteWriteMu.Unlock() + deleteSQL := "DELETE FROM " + tableName + " WHERE key = ?" statement, err := SqliteDB.Prepare(deleteSQL) if err != nil { @@ -94,6 +107,9 @@ func sqliteDeleteRecord(tableName string, key string) error { } func sqliteDeleteAllRecords(tableName string) error { + sqliteWriteMu.Lock() + defer sqliteWriteMu.Unlock() + deleteSQL := "DELETE FROM " + tableName statement, err := SqliteDB.Prepare(deleteSQL) if err != nil { From 2e75e34abb7e1bab94517436b168cfa24d95942f Mon Sep 17 00:00:00 2001 From: VishalDalwadi Date: Wed, 8 Apr 2026 23:32:18 +0530 Subject: [PATCH 4/4] fix(go): use GetMatchingUser in headless callback for azure ad; --- pro/auth/headless_callback.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pro/auth/headless_callback.go b/pro/auth/headless_callback.go index 7cd13423..046b4222 100644 --- a/pro/auth/headless_callback.go +++ b/pro/auth/headless_callback.go @@ -62,8 +62,14 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) { handleOauthUserSignUpApprovalPending(w) return } - user := &schema.User{Username: userClaims.getUserName()} - err = user.Get(r.Context()) + + var user *schema.User + if logic.GetServerSettings().AuthProvider == azure_ad_provider_name { + user, err = GetMatchingUser(userClaims) + } else { + user = &schema.User{Username: userClaims.getUserName()} + err = user.Get(r.Context()) + } if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // user must not exist, so try to make one err = logic.InsertPendingUser(&models.User{