package auth import ( "fmt" "path/filepath" "sync" "github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2/model" fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" ) type EnforcerWrapper struct { mu sync.RWMutex enforcer *casbin.Enforcer store *UserStore modelPath string policyPath string } func NewEnforcer(modelPath, policyPath string, store *UserStore) (*EnforcerWrapper, error) { m, err := model.NewModelFromFile(modelPath) if err != nil { return nil, fmt.Errorf("load casbin model: %w", err) } adapter := fileadapter.NewAdapter(policyPath) enforcer, err := casbin.NewEnforcer(m, adapter) if err != nil { return nil, fmt.Errorf("new enforcer: %w", err) } if err := enforcer.LoadPolicy(); err != nil { return nil, fmt.Errorf("load policy: %w", err) } w := &EnforcerWrapper{enforcer: enforcer, store: store, modelPath: modelPath, policyPath: policyPath} w.syncGrouping() return w, nil } func NewEnforcerWithModelText(modelText string, policyPath string, store *UserStore) (*EnforcerWrapper, error) { m, err := model.NewModelFromString(modelText) if err != nil { return nil, err } var adapter = fileadapter.NewAdapter(policyPath) e, err := casbin.NewEnforcer(m, adapter) if err != nil { return nil, err } _ = e.LoadPolicy() w := &EnforcerWrapper{enforcer: e, store: store} w.syncGrouping() return w, nil } func (w *EnforcerWrapper) syncGrouping() { if w.store == nil { return } w.mu.Lock() defer w.mu.Unlock() for _, u := range w.store.List() { _, _ = w.enforcer.AddGroupingPolicy(u.Username, u.Role) } for _, role := range AllRoles() { _, _ = w.enforcer.AddGroupingPolicy(role, role) } _, _ = w.enforcer.AddGroupingPolicy(RoleGuest, RoleGuest) } func (w *EnforcerWrapper) AddUserRole(username, role string) error { w.mu.Lock() defer w.mu.Unlock() roles, _ := w.enforcer.GetRolesForUser(username) for _, r := range roles { if r != role { _, _ = w.enforcer.RemoveGroupingPolicy(username, r) } } added, err := w.enforcer.AddGroupingPolicy(username, role) if err != nil { return err } if added { _ = w.enforcer.SavePolicy() } return nil } func (w *EnforcerWrapper) RemoveUser(username string) error { w.mu.Lock() defer w.mu.Unlock() _, err := w.enforcer.RemoveFilteredGroupingPolicy(0, username) if err != nil { return err } _ = w.enforcer.SavePolicy() return nil } func (w *EnforcerWrapper) Enforce(sub, obj, act string) (bool, error) { w.mu.RLock() defer w.mu.RUnlock() return w.enforcer.Enforce(sub, obj, act) } func (w *EnforcerWrapper) EnforceWithRole(username, role, obj, act string) (bool, error) { sub := username if sub == "" { sub = role if sub == "" { sub = RoleGuest } } return w.Enforce(sub, obj, act) } func (w *EnforcerWrapper) GetEnforcer() *casbin.Enforcer { return w.enforcer } func (w *EnforcerWrapper) ReloadPolicy() error { w.mu.Lock() defer w.mu.Unlock() return w.enforcer.LoadPolicy() } func (w *EnforcerWrapper) SavePolicy() error { w.mu.Lock() defer w.mu.Unlock() return w.enforcer.SavePolicy() } func (w *EnforcerWrapper) ModelPath() string { return w.modelPath } func (w *EnforcerWrapper) PolicyPath() string { return w.policyPath } func EnsurePolicyFile(path string) string { if path == "" { return "" } abs, _ := filepath.Abs(path) return abs }