mirror of
https://github.com/AlchemillaHQ/Sylve.git
synced 2026-06-14 00:46:34 +03:00
708 lines
18 KiB
Go
708 lines
18 KiB
Go
// SPDX-License-Identifier: BSD-2-Clause
|
|
//
|
|
// Copyright (c) 2025 The FreeBSD Foundation.
|
|
//
|
|
// This software was developed by Hayzam Sherif <hayzam@alchemilla.io>
|
|
// of Alchemilla Ventures Pvt. Ltd. <hello@alchemilla.io>,
|
|
// under sponsorship from the FreeBSD Foundation.
|
|
|
|
package db
|
|
|
|
import (
|
|
"strings"
|
|
|
|
authModels "github.com/alchemillahq/sylve/internal/db/models"
|
|
jailModels "github.com/alchemillahq/sylve/internal/db/models/jail"
|
|
sambaModels "github.com/alchemillahq/sylve/internal/db/models/samba"
|
|
vmModels "github.com/alchemillahq/sylve/internal/db/models/vm"
|
|
"github.com/alchemillahq/sylve/internal/logger"
|
|
"github.com/alchemillahq/sylve/pkg/system"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func Fixups(db *gorm.DB) error {
|
|
runNetworkDeltaMigration(db)
|
|
fixJailNetworkNameIndex(db)
|
|
backfillVMStorageEnableDefaults(db)
|
|
backfillTemplateSourceGuestIDs(db)
|
|
createSylveUnixGroup(db)
|
|
cleanupInvalidTokenRows(db)
|
|
cleanupInvalidAuditUserIDs(db)
|
|
cleanupLegacyDevdEventsTable(db)
|
|
dropSambaSharePathUniqueIndex(db)
|
|
normalizeSambaSharePermissionOverlaps(db)
|
|
backfillFirewallRuleVisibilityDefaults(db)
|
|
backfillUserSystemData(db)
|
|
|
|
return nil
|
|
}
|
|
|
|
func PreMigrationFixups(db *gorm.DB) {
|
|
deduplicateJailHooks(db)
|
|
}
|
|
|
|
func runNetworkDeltaMigration(db *gorm.DB) {
|
|
const name = "network_interface_delta_migration_2"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
// network_interfaces moved to telemetry.db and are migrated there now.
|
|
// Keep this historical fixup idempotent without deleting legacy rows before
|
|
// telemetry migration can copy them.
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func fixJailNetworkNameIndex(db *gorm.DB) {
|
|
const name = "jail_network_name_scope_index_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for fix_jail_network_name_index")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("jail_networks") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
if db.Migrator().HasIndex(&jailModels.Network{}, "idx_jail_network_name") {
|
|
if err := db.Migrator().DropIndex(&jailModels.Network{}, "idx_jail_network_name"); err != nil {
|
|
logger.L.Err(err).Msg("failed dropping legacy jail network global name index")
|
|
return
|
|
}
|
|
}
|
|
|
|
if !db.Migrator().HasIndex(&jailModels.Network{}, "idx_jail_network_name_per_jail") {
|
|
if err := db.Migrator().CreateIndex(&jailModels.Network{}, "idx_jail_network_name_per_jail"); err != nil {
|
|
logger.L.Err(err).Msg("failed creating jail network scoped name index")
|
|
return
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func backfillVMStorageEnableDefaults(db *gorm.DB) {
|
|
const name = "vm_storage_enable_default_true_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for backfill_vm_storage_enable_defaults")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("vm_storages") || !db.Migrator().HasColumn("vm_storages", "enable") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
if err := db.Exec(`UPDATE vm_storages SET enable = 1`).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed to backfill vm_storages.enable default values")
|
|
return
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func createSylveUnixGroup(db *gorm.DB) {
|
|
var count int64
|
|
if err := db.
|
|
Table("groups").
|
|
Where("name = ?", "sylve_g").
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed checking for sylve unix group")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
exists := system.UnixGroupExists("sylve_g")
|
|
if !exists {
|
|
if err := system.CreateUnixGroup("sylve_g"); err != nil {
|
|
logger.L.Err(err).Msg("failed creating sylve unix group")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err := db.Exec(`INSERT INTO groups (name, notes, created_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`, "sylve_g", "Default sylve unix group").Error; err != nil {
|
|
logger.L.Err(err).Msg("failed creating sylve unix group")
|
|
return
|
|
}
|
|
|
|
exists := system.UnixGroupExists("sylve_g")
|
|
if !exists {
|
|
if err := system.CreateUnixGroup("sylve_g"); err != nil {
|
|
logger.L.Err(err).Msg("failed creating sylve unix group")
|
|
}
|
|
}
|
|
}
|
|
|
|
func cleanupInvalidTokenRows(db *gorm.DB) {
|
|
const name = "cleanup_invalid_token_rows_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for cleanup_invalid_token_rows")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("tokens") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
query := `DELETE FROM tokens WHERE user_id < 0`
|
|
args := []any{}
|
|
if db.Migrator().HasTable("pam_identities") {
|
|
query = `DELETE FROM tokens WHERE user_id < 0 OR (auth_type = ? AND user_id NOT IN (SELECT id FROM pam_identities))`
|
|
args = append(args, "pam")
|
|
}
|
|
|
|
result := db.Exec(query, args...)
|
|
if result.Error != nil {
|
|
logger.L.Err(result.Error).Msg("failed cleaning up invalid token rows")
|
|
return
|
|
}
|
|
|
|
if result.RowsAffected > 0 {
|
|
logger.L.Warn().Msgf("Deleted %d invalid token rows", result.RowsAffected)
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func cleanupInvalidAuditUserIDs(db *gorm.DB) {
|
|
const name = "cleanup_invalid_audit_user_ids_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for cleanup_invalid_audit_user_ids")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("audit_records") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
result := db.Exec(`UPDATE audit_records SET user_id = NULL WHERE user_id < 0`)
|
|
if result.Error != nil {
|
|
logger.L.Err(result.Error).Msg("failed cleaning up invalid audit user IDs")
|
|
return
|
|
}
|
|
|
|
if result.RowsAffected > 0 {
|
|
logger.L.Warn().Msgf("Nullified %d invalid audit user IDs", result.RowsAffected)
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func cleanupLegacyDevdEventsTable(db *gorm.DB) {
|
|
const name = "drop_legacy_devd_events_table_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for cleanup_legacy_devd_events_table")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("devd_events") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
if err := db.Migrator().DropTable("devd_events"); err != nil {
|
|
logger.L.Err(err).Msg("failed dropping legacy devd_events table")
|
|
return
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func dropSambaSharePathUniqueIndex(db *gorm.DB) {
|
|
const name = "drop_samba_share_path_unique_index_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for drop_samba_share_path_unique_index")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable(&sambaModels.SambaShare{}) {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
indexes, err := db.Migrator().GetIndexes(&sambaModels.SambaShare{})
|
|
if err != nil {
|
|
logger.L.Err(err).Msg("failed retrieving samba_shares indexes")
|
|
return
|
|
}
|
|
|
|
for _, idx := range indexes {
|
|
unique, ok := idx.Unique()
|
|
if !ok || !unique {
|
|
continue
|
|
}
|
|
|
|
columns := idx.Columns()
|
|
if len(columns) != 1 || !strings.EqualFold(columns[0], "path") {
|
|
continue
|
|
}
|
|
|
|
if err := db.Migrator().DropIndex(&sambaModels.SambaShare{}, idx.Name()); err != nil {
|
|
logger.L.Err(err).Str("index", idx.Name()).Msg("failed dropping samba_shares path unique index")
|
|
return
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func normalizeSambaSharePermissionOverlaps(db *gorm.DB) {
|
|
const name = "normalize_samba_share_permission_overlaps_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for normalize_samba_share_permission_overlaps")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable(&sambaModels.SambaShare{}) {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
var shares []sambaModels.SambaShare
|
|
if err := db.
|
|
Preload("ReadOnlyUsers").
|
|
Preload("WriteableUsers").
|
|
Preload("ReadOnlyGroups").
|
|
Preload("WriteableGroups").
|
|
Find(&shares).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed loading samba shares for permission overlap normalization")
|
|
return
|
|
}
|
|
|
|
for i := range shares {
|
|
share := shares[i]
|
|
|
|
writeUserIDs := make(map[uint]struct{}, len(share.WriteableUsers))
|
|
for _, user := range share.WriteableUsers {
|
|
writeUserIDs[user.ID] = struct{}{}
|
|
}
|
|
|
|
writeGroupIDs := make(map[uint]struct{}, len(share.WriteableGroups))
|
|
for _, group := range share.WriteableGroups {
|
|
writeGroupIDs[group.ID] = struct{}{}
|
|
}
|
|
|
|
filteredReadUsers := make([]authModels.User, 0, len(share.ReadOnlyUsers))
|
|
userChanged := false
|
|
for _, user := range share.ReadOnlyUsers {
|
|
if _, exists := writeUserIDs[user.ID]; exists {
|
|
userChanged = true
|
|
continue
|
|
}
|
|
filteredReadUsers = append(filteredReadUsers, user)
|
|
}
|
|
|
|
filteredReadGroups := make([]authModels.Group, 0, len(share.ReadOnlyGroups))
|
|
groupChanged := false
|
|
for _, group := range share.ReadOnlyGroups {
|
|
if _, exists := writeGroupIDs[group.ID]; exists {
|
|
groupChanged = true
|
|
continue
|
|
}
|
|
filteredReadGroups = append(filteredReadGroups, group)
|
|
}
|
|
|
|
if userChanged {
|
|
if err := db.Model(&share).Association("ReadOnlyUsers").Replace(filteredReadUsers); err != nil {
|
|
logger.L.Err(err).Int("share_id", share.ID).Msg("failed normalizing samba share user permission overlap")
|
|
return
|
|
}
|
|
}
|
|
|
|
if groupChanged {
|
|
if err := db.Model(&share).Association("ReadOnlyGroups").Replace(filteredReadGroups); err != nil {
|
|
logger.L.Err(err).Int("share_id", share.ID).Msg("failed normalizing samba share group permission overlap")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func backfillFirewallRuleVisibilityDefaults(db *gorm.DB) {
|
|
const name = "backfill_firewall_rule_visibility_defaults_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for backfill_firewall_rule_visibility_defaults")
|
|
return
|
|
}
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if db.Migrator().HasTable("firewall_traffic_rules") && db.Migrator().HasColumn("firewall_traffic_rules", "visible") {
|
|
if err := db.Exec(`UPDATE firewall_traffic_rules SET visible = 1`).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed to backfill firewall_traffic_rules.visible")
|
|
return
|
|
}
|
|
}
|
|
if db.Migrator().HasTable("firewall_nat_rules") && db.Migrator().HasColumn("firewall_nat_rules", "visible") {
|
|
if err := db.Exec(`UPDATE firewall_nat_rules SET visible = 1`).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed to backfill firewall_nat_rules.visible")
|
|
return
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
func backfillTemplateSourceGuestIDs(db *gorm.DB) {
|
|
const name = "template_source_guest_id_backfill_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for backfill_template_source_guest_ids")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if db.Migrator().HasTable(&vmModels.VMTemplate{}) && db.Migrator().HasTable(&vmModels.VM{}) {
|
|
var vms []vmModels.VM
|
|
if err := db.Model(&vmModels.VM{}).Select("name", "rid").Find(&vms).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed loading vm sources for template source id backfill")
|
|
return
|
|
}
|
|
|
|
ridByName := make(map[string]uint, len(vms))
|
|
ambiguous := make(map[string]struct{})
|
|
for _, vm := range vms {
|
|
sourceName := strings.TrimSpace(vm.Name)
|
|
if sourceName == "" || vm.RID == 0 {
|
|
continue
|
|
}
|
|
|
|
if existing, exists := ridByName[sourceName]; exists && existing != vm.RID {
|
|
ambiguous[sourceName] = struct{}{}
|
|
continue
|
|
}
|
|
|
|
ridByName[sourceName] = vm.RID
|
|
}
|
|
|
|
var templates []vmModels.VMTemplate
|
|
if err := db.Model(&vmModels.VMTemplate{}).
|
|
Select("id", "source_vm_name", "source_vm_rid").
|
|
Where("source_vm_rid = ?", 0).
|
|
Find(&templates).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed loading vm templates for source id backfill")
|
|
return
|
|
}
|
|
|
|
for _, tpl := range templates {
|
|
sourceName := strings.TrimSpace(tpl.SourceVMName)
|
|
if sourceName == "" {
|
|
continue
|
|
}
|
|
|
|
if _, exists := ambiguous[sourceName]; exists {
|
|
continue
|
|
}
|
|
|
|
rid, exists := ridByName[sourceName]
|
|
if !exists || rid == 0 {
|
|
continue
|
|
}
|
|
|
|
if err := db.Model(&vmModels.VMTemplate{}).
|
|
Where("id = ?", tpl.ID).
|
|
Update("source_vm_rid", rid).Error; err != nil {
|
|
logger.L.Err(err).Uint("template_id", tpl.ID).Msg("failed backfilling vm template source rid")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if db.Migrator().HasTable(&jailModels.JailTemplate{}) && db.Migrator().HasTable(&jailModels.Jail{}) {
|
|
var jails []jailModels.Jail
|
|
if err := db.Model(&jailModels.Jail{}).Select("name", "ct_id").Find(&jails).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed loading jail sources for template source id backfill")
|
|
return
|
|
}
|
|
|
|
ctidByName := make(map[string]uint, len(jails))
|
|
ambiguous := make(map[string]struct{})
|
|
for _, jail := range jails {
|
|
sourceName := strings.TrimSpace(jail.Name)
|
|
if sourceName == "" || jail.CTID == 0 {
|
|
continue
|
|
}
|
|
|
|
if existing, exists := ctidByName[sourceName]; exists && existing != jail.CTID {
|
|
ambiguous[sourceName] = struct{}{}
|
|
continue
|
|
}
|
|
|
|
ctidByName[sourceName] = jail.CTID
|
|
}
|
|
|
|
var templates []jailModels.JailTemplate
|
|
if err := db.Model(&jailModels.JailTemplate{}).
|
|
Select("id", "source_jail_name", "source_jail_ctid").
|
|
Where("source_jail_ctid = ?", 0).
|
|
Find(&templates).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed loading jail templates for source id backfill")
|
|
return
|
|
}
|
|
|
|
for _, tpl := range templates {
|
|
sourceName := strings.TrimSpace(tpl.SourceJailName)
|
|
if sourceName == "" {
|
|
continue
|
|
}
|
|
|
|
if _, exists := ambiguous[sourceName]; exists {
|
|
continue
|
|
}
|
|
|
|
ctid, exists := ctidByName[sourceName]
|
|
if !exists || ctid == 0 {
|
|
continue
|
|
}
|
|
|
|
if err := db.Model(&jailModels.JailTemplate{}).
|
|
Where("id = ?", tpl.ID).
|
|
Update("source_jail_ctid", ctid).Error; err != nil {
|
|
logger.L.Err(err).Uint("template_id", tpl.ID).Msg("failed backfilling jail template source ctid")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* A previous migration omitted a primary key on the jail_hooks table,
|
|
* which caused runaway duplicate entries during association updates and
|
|
* made it impossible to target specific hooks.
|
|
* * This fixup deduplicates the existing entries so GORM can safely apply
|
|
* an auto-incrementing primary key during the subsequent AutoMigrate pass.
|
|
*/
|
|
func deduplicateJailHooks(db *gorm.DB) {
|
|
const name = "deduplicate_jail_hooks_1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for deduplicate_jail_hooks")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
if !db.Migrator().HasTable("jail_hooks") {
|
|
db.Table("migrations").Create(map[string]any{"name": name})
|
|
return
|
|
}
|
|
|
|
err := db.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Exec(`
|
|
CREATE TABLE jail_hooks_temp (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
jid INTEGER,
|
|
phase TEXT,
|
|
enabled BOOLEAN,
|
|
script TEXT
|
|
)
|
|
`).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Exec(`
|
|
INSERT INTO jail_hooks_temp (jid, phase, enabled, script)
|
|
SELECT DISTINCT jid, phase, enabled, script FROM jail_hooks
|
|
`).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Exec(`DROP TABLE jail_hooks`).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := tx.Exec(`ALTER TABLE jail_hooks_temp RENAME TO jail_hooks`).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
logger.L.Err(err).Msg("failed deduplicating jail hooks")
|
|
return
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
|
|
logger.L.Info().Msg("Deduplicated jail hooks and migrated schema")
|
|
}
|
|
|
|
func backfillUserSystemData(db *gorm.DB) {
|
|
const name = "backfill_user_system_data_v1"
|
|
|
|
var count int64
|
|
if err := db.
|
|
Table("migrations").
|
|
Where("name = ?", name).
|
|
Count(&count).Error; err != nil {
|
|
logger.L.Err(err).Msg("migration check failed for backfill_user_system_data")
|
|
return
|
|
}
|
|
|
|
if count > 0 {
|
|
return
|
|
}
|
|
|
|
var users []authModels.User
|
|
if err := db.Find(&users).Error; err != nil {
|
|
logger.L.Err(err).Msg("failed to list users for backfill_user_system_data")
|
|
return
|
|
}
|
|
|
|
for i := range users {
|
|
updates := map[string]any{}
|
|
|
|
if users[i].UID == 0 {
|
|
uid, shell, err := system.GetUnixUserInfo(users[i].Username)
|
|
if err != nil {
|
|
logger.L.Warn().Msgf("backfill_user_system_data: failed to get info for %s: %v", users[i].Username, err)
|
|
} else {
|
|
if uid > 0 {
|
|
updates["uid"] = uid
|
|
}
|
|
if shell != "" {
|
|
updates["shell"] = shell
|
|
}
|
|
}
|
|
}
|
|
|
|
if users[i].HomeDirPerms == 0 {
|
|
updates["home_dir_perms"] = 493
|
|
}
|
|
|
|
if len(updates) > 0 {
|
|
if err := db.Model(&users[i]).Updates(updates).Error; err != nil {
|
|
logger.L.Warn().Msgf("backfill_user_system_data: failed to update user %s: %v", users[i].Username, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
db.Table("migrations").Create(map[string]any{
|
|
"name": name,
|
|
})
|
|
}
|