package store import ( "context" "fmt" "strings" "atlas9.dev/c/core" "atlas9.dev/c/core/dbi" "atlas9.dev/c/core/iam" "atlas9.dev/c/demo/lib/access" ) type SqliteBotGrantStore struct { db dbi.DBI guard access.Guard } var _ iam.BotGrantStore = (*SqliteBotGrantStore)(nil) func NewSqliteBotGrantStore(db dbi.DBI, guard access.Guard) *SqliteBotGrantStore { return &SqliteBotGrantStore{db: db, guard: guard} } func (s *SqliteBotGrantStore) Add(ctx context.Context, g iam.BotGrant) error { if err := s.guard.Check(ctx, iam.CapGrantsAdd, g.Tenant, ""); err != nil { return err } _, err := s.db.Exec(ctx, ` INSERT INTO bot_grants (tenant, bot, role, path) VALUES ($1, $2, $3, $4) ON CONFLICT (tenant, bot, role, path) DO NOTHING `, g.Tenant, g.Bot, g.Role, g.Path) // The FK to bots rejects a grant for a bot that doesn't exist. if err != nil && strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { return fmt.Errorf("bot does not exist") } return err } func (s *SqliteBotGrantStore) Remove(ctx context.Context, g iam.BotGrant) error { if err := s.guard.Check(ctx, iam.CapGrantsRemove, g.Tenant, ""); err != nil { return err } _, err := s.db.Exec(ctx, ` DELETE FROM bot_grants WHERE tenant = $1 AND bot = $2 AND role = $3 AND path = $4 `, g.Tenant, g.Bot, g.Role, g.Path) return err } func (s *SqliteBotGrantStore) ListByTenant(ctx context.Context, tenant core.ID, page core.PageReq) (core.Page[iam.BotGrant], error) { var out core.Page[iam.BotGrant] if err := s.guard.Check(ctx, iam.CapGrantsList, tenant, ""); err != nil { return out, err } limit := page.Limit if limit <= 0 { limit = 100 } rows, err := s.db.Query(ctx, ` SELECT tenant, bot, role, path FROM bot_grants WHERE tenant = $1 ORDER BY bot, role, path LIMIT $2 `, tenant, limit) if err != nil { return out, err } defer rows.Close() for rows.Next() { var g iam.BotGrant if err := rows.Scan(&g.Tenant, &g.Bot, &g.Role, &g.Path); err != nil { return out, err } out.Items = append(out.Items, g) } return out, rows.Err() }