package admin import ( "errors" "fmt" "log/slog" "strconv" casbin "github.com/casbin/casbin/v2" ) // Servicer container for dependencies and functions type Servicer struct { *casbin.SyncedEnforcer Conf *ServerRepos serverConfigPath string reposDir string mgmtRepo bool } // Reload reoload server config and sync policies func (s *Servicer) Reload() { tmpConfig, err := loadServerConfig(s.mgmtRepo, s.reposDir, s.serverConfigPath) if err != nil { // log.error slog.Error("failed to load config", "error", err) slog.Error("refusing to reload config") return } slog.Debug("config base after load", "path", tmpConfig.basePath) // copy oldConfig := *s.Conf slog.Debug("config base before copy", "path", s.Conf.basePath) s.Conf = tmpConfig slog.Debug("config base after copy", "path", s.Conf.basePath) if err := s.InitServer(); err != nil { slog.Error("couldn't init server with new config, falling back", slog.Any("error", err)) s.Conf = &oldConfig if err := s.InitServer(); err != nil { slog.Error("couldn't init server with old config, falling back", slog.Any("error", err)) panic("new and old config couldn't init server, no available config to run") } slog.Error("server has fallen back to old config but it lives in memory only, in fragile state") } } // InitServer initialize a git server and configure func (s *Servicer) InitServer() error { policies := s.Conf.ServerPolicies() numAdded := 0 slog.Debug(fmt.Sprintf("number of repos %s", strconv.Itoa(len(s.Conf.Repos)))) for _, policy := range policies { added, err := s.AddPolicy(policy[0], policy[1], policy[2]) if err != nil { // log.error slog.Error("error adding policy", "role", policy[0], "slug", policy[1], "action", policy[2], "err", err) continue } if added { numAdded++ } } slog.Info("policy generated", "added", numAdded) if err := s.SavePolicy(); err != nil { return fmt.Errorf("policy couldn't be saved %w", err) } slog.Info("policy saved") if err := s.LoadPolicy(); err != nil { return fmt.Errorf("cloudn't load policy %w", err) } slog.Info("policy loaded") slog.Debug("init server", "repoDir", s.reposDir) slog.Debug("init server", "repoDir", s.Conf.basePath) if err := s.Conf.ConfigureRepos(); err != nil { return fmt.Errorf("couldn't configure repos %w", err) } slog.Info("configured repos") return nil } // NewService create a new admin service, load config, and generate policies func NewService(modelPath, policyPath, serverConfigPath, reposDir string, mgmtRepo bool) (*Servicer, error) { workingPolicyPath, err := setupPolicyFile(policyPath) if err != nil { return &Servicer{}, err } slog.Debug(fmt.Sprintf("policy path %s", workingPolicyPath)) enf, err := casbin.NewSyncedEnforcer(modelPath, workingPolicyPath) if err != nil { return &Servicer{}, fmt.Errorf("couldn't load the enforcer encountered the following error: %w", err) } conf, err := loadServerConfig(mgmtRepo, reposDir, serverConfigPath) if errors.Is(err, ErrMgmtRepoNotFound) { slog.Info("no server config found, using default") conf = defaultServerConfig conf.basePath = reposDir } else if err != nil { return &Servicer{}, fmt.Errorf("coudln't load server config. %w", err) } svc := &Servicer{ enf, conf, serverConfigPath, reposDir, mgmtRepo, } if err := svc.InitServer(); err != nil { return nil, err } return svc, nil }