Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion taco/cmd/statesman/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func main() {

// Load configuration from environment variables into our struct.
var queryCfg query.Config
err := envconfig.Process("taco", &queryCfg) // The prefix "TACO" will be used for all vars.
err := envconfig.Process("opentaco", &queryCfg) // The prefix "TACO" will be used for all vars.
if err != nil {
log.Fatalf("Failed to process configuration: %v", err)
}
Expand Down
57 changes: 28 additions & 29 deletions taco/internal/api/org_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"log/slog"
"net/http"
"context"
"fmt"

"github.com/diggerhq/digger/opentaco/internal/domain"
"github.com/diggerhq/digger/opentaco/internal/rbac"
Expand Down Expand Up @@ -96,40 +95,17 @@ func (h *OrgHandler) CreateOrganization(c echo.Context) error {
)

// ========================================
// Use transaction to create org + init RBAC atomically
// Create org first, then init RBAC (SQLite-friendly)
// ========================================
var org *domain.Organization

// Create organization in transaction
err := h.orgRepo.WithTransaction(ctx, func(ctx context.Context, txRepo domain.OrganizationRepository) error {
// Create organization within transaction
createdOrg, err := txRepo.Create(ctx, req.OrgID, req.Name, userIDStr)
if err != nil {
return err
}
org = createdOrg

// Initialize RBAC within the same transaction
if h.rbacManager != nil {
slog.Info("Initializing RBAC for new organization",
"orgID", req.OrgID,
"adminUser", userIDStr,
)

if err := h.rbacManager.InitializeRBAC(ctx, userIDStr, emailStr); err != nil {
// IMPORTANT: Returning error here will rollback the entire transaction
slog.Error("Failed to initialize RBAC, rolling back org creation",
"orgID", req.OrgID,
"error", err,
)
return fmt.Errorf("failed to initialize RBAC: %w", err)
}

slog.Info("RBAC initialized successfully",
"orgID", req.OrgID,
"adminUser", userIDStr,
)
}

return nil
})

Expand All @@ -146,8 +122,7 @@ func (h *OrgHandler) CreateOrganization(c echo.Context) error {
})
}

// Any other error (including RBAC init failure) returns 500
slog.Error("Failed to create organization with RBAC",
slog.Error("Failed to create organization",
"orgID", req.OrgID,
"error", err,
)
Expand All @@ -157,7 +132,31 @@ func (h *OrgHandler) CreateOrganization(c echo.Context) error {
})
}

// Success - both org and RBAC were created
// Initialize RBAC after org creation (outside transaction for SQLite compatibility)
if h.rbacManager != nil {
slog.Info("Initializing RBAC for new organization",
"orgID", req.OrgID,
"adminUser", userIDStr,
)

if err := h.rbacManager.InitializeRBAC(ctx, userIDStr, emailStr); err != nil {
// Org was created but RBAC failed - log warning but don't fail the request
// User can retry RBAC initialization or assign roles manually
slog.Warn("Organization created but RBAC initialization failed",
"orgID", req.OrgID,
"error", err,
"recommendation", "RBAC can be initialized later via /rbac/init endpoint",
)
// Continue with success response - org was created
} else {
slog.Info("RBAC initialized successfully",
"orgID", req.OrgID,
"adminUser", userIDStr,
)
}
}

// Success - org created (and RBAC initialized if available)
return c.JSON(http.StatusCreated, CreateOrgResponse{
OrgID: org.OrgID,
Name: org.Name,
Expand Down
4 changes: 2 additions & 2 deletions taco/internal/query/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type SQLiteConfig struct {
Path string `envconfig:"DB_PATH" default:"./data/taco.db"`// if we call it PATH at the struct level, it will pick up the terminal path
Cache string `envconfig:"CACHE" default:"shared"`
BusyTimeout time.Duration `envconfig:"BUSY_TIMEOUT" default:"5s"`
MaxOpenConns int `envconfig:"MAX_OPEN_CONNS" default:"1"`
MaxIdleConns int `envconfig:"MAX_IDLE_CONNS" default:"1"`
MaxOpenConns int `envconfig:"MAX_OPEN_CONNS" default:"25"`
MaxIdleConns int `envconfig:"MAX_IDLE_CONNS" default:"10"`
PragmaJournalMode string `envconfig:"PRAGMA_JOURNAL_MODE" default:"WAL"`
PragmaForeignKeys string `envconfig:"PRAGMA_FOREIGN_KEYS" default:"ON"`
PragmaBusyTimeout string `envconfig:"PRAGMA_BUSY_TIMEOUT" default:"5000"`
Expand Down
8 changes: 8 additions & 0 deletions taco/internal/query/sqlite/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ func NewSQLiteQueryStore(cfg query.SQLiteConfig) (query.Store, error) {
return nil, fmt.Errorf("apply busy_timeout: %w", err)
}

// Configure connection pool settings
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("get underlying sql.DB: %w", err)
}
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)

// Create the common SQLStore with our configured DB object, breaking the cycle.
return common.NewSQLStore(db)
}
Expand Down
17 changes: 6 additions & 11 deletions taco/internal/rbac/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ func (h *Handler) DeleteRole(c echo.Context) error {
// Helper functions

func (h *Handler) getPrincipalFromToken(c echo.Context) (Principal, error) {
// First check if principal is already in context (webhook auth sets this)
if principal, ok := PrincipalFromContext(c.Request().Context()); ok {
return principal, nil
}

// Fall back to JWT token verification for public API routes
authz := c.Request().Header.Get("Authorization")
if !strings.HasPrefix(authz, "Bearer ") {
return Principal{}, echo.NewHTTPError(http.StatusUnauthorized, "missing bearer token")
Expand All @@ -345,19 +351,8 @@ func (h *Handler) getPrincipalFromToken(c echo.Context) (Principal, error) {
return Principal{}, echo.NewHTTPError(http.StatusInternalServerError, "auth not configured")
}

// Debug: check signer state
fmt.Printf("[RBAC DEBUG] Signer nil? %t\n", h.signer == nil)
fmt.Printf("[RBAC DEBUG] Signer addr: %p\n", h.signer)

claims, err := h.signer.VerifyAccess(token)
if err != nil {
// Debug: log the verification failure
fmt.Printf("[RBAC DEBUG] Token verification failed: %v\n", err)
tokenPreview := token
if len(token) > 50 {
tokenPreview = token[:50] + "..."
}
fmt.Printf("[RBAC DEBUG] Token preview: %s\n", tokenPreview)
return Principal{}, echo.NewHTTPError(http.StatusUnauthorized, "invalid token")
}

Expand Down
Loading