Documentation
¶
Overview ¶
Package rls provides PostgreSQL Row-Level Security helpers for CoreForge.
Index ¶
- Variables
- func BypassRLS(roleName string) string
- func ContextWithTenant(ctx context.Context, tenantID uuid.UUID) context.Context
- func ContextWithTenantAndUser(ctx context.Context, tenantID, userID uuid.UUID) context.Context
- func ContextWithUser(ctx context.Context, userID uuid.UUID) context.Context
- func HasTenant(ctx context.Context) bool
- func HasUser(ctx context.Context) bool
- func NoBypassRLS(roleName string) string
- func TenantIDFromContext(ctx context.Context) uuid.UUID
- func TenantIDString(ctx context.Context) string
- func UserIDFromContext(ctx context.Context) uuid.UUID
- func UserIDString(ctx context.Context) string
- func WithTenant(ctx context.Context, db *sql.DB, helper *Helper, tenantID, userID uuid.UUID, ...) error
- func WithTenantFromContext(ctx context.Context, db *sql.DB, helper *Helper, fn TxFunc) error
- func WithTenantOpts(ctx context.Context, db *sql.DB, helper *Helper, opts *TxOptions, fn TxFunc) error
- type Config
- type ConnWithRLS
- type ContextInjector
- type DBWithRLS
- type EntDriver
- type EntHook
- type Executor
- type Helper
- func (h *Helper) ClearContext(ctx context.Context, db Executor) error
- func (h *Helper) SetContext(ctx context.Context, db Executor, tenantID, userID string) error
- func (h *Helper) SetTenant(ctx context.Context, db Executor, tenantID string) error
- func (h *Helper) SetUser(ctx context.Context, db Executor, userID string) error
- type Middleware
- type MigrationConfig
- type Migrator
- func (m *Migrator) DisableRLS(ctx context.Context, table string) error
- func (m *Migrator) EnableRLS(ctx context.Context, table string) error
- func (m *Migrator) GenerateMigrationSQL() string
- func (m *Migrator) GenerateRollbackSQL() string
- func (m *Migrator) GrantBypass(ctx context.Context, role string) error
- func (m *Migrator) MigrateCoreForge(ctx context.Context) error
- func (m *Migrator) RevokeBypass(ctx context.Context, role string) error
- func (m *Migrator) VerifyRLS(ctx context.Context) ([]TableRLSStatus, error)
- type PolicySQL
- func (p *PolicySQL) CreateAllPolicies() string
- func (p *PolicySQL) CreateDeletePolicy() string
- func (p *PolicySQL) CreateInsertPolicy() string
- func (p *PolicySQL) CreateSelectPolicy() string
- func (p *PolicySQL) CreateUpdatePolicy() string
- func (p *PolicySQL) DropAllPolicies() string
- func (p *PolicySQL) DropPolicy(operation string) string
- func (p *PolicySQL) EnableRLS() string
- func (p *PolicySQL) ForceRLS() string
- type TableRLSStatus
- type TestHelper
- func (th *TestHelper) AsTenant(tenantID uuid.UUID, fn func(ctx context.Context, tx *sql.Tx))
- func (th *TestHelper) AsUser(tenantID, userID uuid.UUID, fn func(ctx context.Context, tx *sql.Tx))
- func (th *TestHelper) AssertCanRead(tenantID, userID uuid.UUID, table, whereClause string, args ...any)
- func (th *TestHelper) AssertCannotRead(tenantID, userID uuid.UUID, table, whereClause string, args ...any)
- func (th *TestHelper) AssertTenantIsolation(table, insertCol, valueCol string)
- func (th *TestHelper) SetupTestTenant(name string) (tenantID uuid.UUID, cleanup func())
- func (th *TestHelper) SetupTestUser(tenantID uuid.UUID, email, role string) (userID uuid.UUID, cleanup func())
- func (th *TestHelper) WithoutRLS(fn func(ctx context.Context, tx *sql.Tx))
- type TxFunc
- type TxManager
- func (m *TxManager) Begin(ctx context.Context, tenantID, userID uuid.UUID) (*sql.Tx, error)
- func (m *TxManager) BeginFromContext(ctx context.Context) (*sql.Tx, error)
- func (m *TxManager) WithTenant(ctx context.Context, tenantID, userID uuid.UUID, fn TxFunc) error
- func (m *TxManager) WithTenantFromContext(ctx context.Context, fn TxFunc) error
- type TxOptions
Constants ¶
This section is empty.
Variables ¶
var CoreForgeTables = []string{
"cf_memberships",
}
CoreForgeTables returns the list of CoreForge tables that need RLS. These are the tables with tenant-scoped data.
Functions ¶
func BypassRLS ¶
BypassRLS returns SQL to grant RLS bypass to a role. This is typically used for admin/migration roles.
func ContextWithTenant ¶
ContextWithTenant returns a new context with the tenant ID attached.
func ContextWithTenantAndUser ¶
ContextWithTenantAndUser returns a new context with both tenant and user IDs.
func ContextWithUser ¶
ContextWithUser returns a new context with the user ID attached.
func NoBypassRLS ¶
NoBypassRLS returns SQL to revoke RLS bypass from a role.
func TenantIDFromContext ¶
TenantIDFromContext extracts the tenant ID from the context. Returns uuid.Nil if no tenant ID is present.
func TenantIDString ¶
TenantIDString returns the tenant ID as a string, or empty string if not present.
func UserIDFromContext ¶
UserIDFromContext extracts the user ID from the context. Returns uuid.Nil if no user ID is present.
func UserIDString ¶
UserIDString returns the user ID as a string, or empty string if not present.
func WithTenant ¶
func WithTenant(ctx context.Context, db *sql.DB, helper *Helper, tenantID, userID uuid.UUID, fn TxFunc) error
WithTenant executes a function within a transaction with RLS context set. The tenant and user session variables are set before the function executes, and the transaction is committed if the function returns nil.
Example:
err := rls.WithTenant(ctx, db, helper, tenantID, userID, func(tx *sql.Tx) error {
_, err := tx.ExecContext(ctx, "INSERT INTO items (name) VALUES ($1)", name)
return err
})
func WithTenantFromContext ¶
WithTenantFromContext executes a function within a transaction using tenant and user IDs from the context.
Types ¶
type Config ¶
type Config struct {
// TenantColumn is the name of the column used for tenant isolation.
// Defaults to "organization_id".
TenantColumn string
// UserColumn is the name of the column used for user identification.
// Defaults to "user_id".
UserColumn string
// SessionVariable is the PostgreSQL session variable for the current tenant.
// Defaults to "app.current_tenant".
SessionVariable string
// UserSessionVariable is the PostgreSQL session variable for the current user.
// Defaults to "app.current_user".
UserSessionVariable string
}
Config holds RLS configuration.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns the default RLS configuration.
type ConnWithRLS ¶
ConnWithRLS wraps sql.Conn to ensure RLS context is set.
func GetConnWithRLS ¶
GetConnWithRLS gets a connection from the pool with RLS context set.
func NewConnWithRLS ¶
NewConnWithRLS wraps a connection with RLS context injection.
type ContextInjector ¶
type ContextInjector struct {
// contains filtered or unexported fields
}
ContextInjector creates a function that can be used to inject RLS context into database connections. This is useful for connection pool hooks.
func NewContextInjector ¶
func NewContextInjector(cfg *Config) *ContextInjector
NewContextInjector creates a new context injector.
func (*ContextInjector) InjectContext ¶
InjectContext sets RLS session variables on a connection. Use this with connection pool acquire hooks.
type DBWithRLS ¶
DBWithRLS wraps a database connection to automatically set RLS context for each connection obtained from the pool.
type EntDriver ¶
type EntDriver struct {
// contains filtered or unexported fields
}
EntDriver wraps an Ent SQL driver to automatically set RLS context. This ensures RLS is set for every database operation.
func NewEntDriver ¶
NewEntDriver creates a new RLS-aware Ent driver wrapper.
type EntHook ¶
type EntHook struct {
// contains filtered or unexported fields
}
EntHook provides integration with Ent ORM for automatic RLS context.
Usage with Ent:
hook := rls.NewEntHook(db, rls.DefaultConfig())
client := ent.NewClient(
ent.Driver(drv),
ent.Hook(hook.SetContext()),
)
func NewEntHook ¶
NewEntHook creates a new Ent RLS hook.
type Executor ¶
type Executor interface {
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}
Executor is an interface for executing SQL queries. Implemented by *sql.DB, *sql.Tx, and similar types.
type Helper ¶
type Helper struct {
// contains filtered or unexported fields
}
Helper provides RLS operations for PostgreSQL.
func (*Helper) ClearContext ¶
ClearContext clears the tenant and user session variables.
func (*Helper) SetContext ¶
SetContext sets both tenant and user in the database session.
type Middleware ¶
type Middleware struct {
// contains filtered or unexported fields
}
Middleware provides HTTP middleware that automatically sets PostgreSQL RLS context from JWT claims.
func NewMiddleware ¶
func NewMiddleware(db *sql.DB, cfg *Config) *Middleware
NewMiddleware creates a new RLS middleware.
func (*Middleware) RequireTenant ¶
func (m *Middleware) RequireTenant() func(http.Handler) http.Handler
RequireTenant returns middleware that ensures a tenant context is present. Returns 403 Forbidden if no organization is in the JWT claims.
func (*Middleware) SetRLSContext ¶
func (m *Middleware) SetRLSContext() func(http.Handler) http.Handler
SetRLSContext returns middleware that sets PostgreSQL session variables from the authenticated user's JWT claims.
This middleware should be used AFTER authentication middleware. It extracts the user ID and organization ID from the JWT claims and sets them as PostgreSQL session variables for RLS policies.
Usage with Chi:
r.Use(middleware.ChiAuth(jwtService)) r.Use(rlsMiddleware.SetRLSContext())
type MigrationConfig ¶
type MigrationConfig struct {
// Tables to apply RLS to. If empty, applies to CoreForge tables.
Tables []string
// TenantColumn is the column name for tenant isolation.
// Defaults to "organization_id".
TenantColumn string
// SessionVariable is the PostgreSQL session variable for tenant ID.
// Defaults to "app.current_tenant".
SessionVariable string
// BypassRoles are PostgreSQL roles that should bypass RLS.
// Typically includes migration and admin roles.
BypassRoles []string
// AppRole is the PostgreSQL role used by the application.
// RLS policies will be created for this role.
AppRole string
}
MigrationConfig configures RLS migration behavior.
func DefaultMigrationConfig ¶
func DefaultMigrationConfig() *MigrationConfig
DefaultMigrationConfig returns default migration configuration.
type Migrator ¶
type Migrator struct {
// contains filtered or unexported fields
}
Migrator handles RLS migration for CoreForge tables.
func NewMigrator ¶
func NewMigrator(db *sql.DB, cfg *MigrationConfig) *Migrator
NewMigrator creates a new RLS migrator.
func (*Migrator) DisableRLS ¶
DisableRLS disables RLS on a table and drops policies.
func (*Migrator) GenerateMigrationSQL ¶
GenerateMigrationSQL generates SQL for RLS migration without executing it. Useful for reviewing or including in migration files.
func (*Migrator) GenerateRollbackSQL ¶
GenerateRollbackSQL generates SQL to undo RLS migration.
func (*Migrator) GrantBypass ¶
GrantBypass grants RLS bypass to a role.
func (*Migrator) MigrateCoreForge ¶
MigrateCoreForge applies RLS policies to CoreForge tables.
func (*Migrator) RevokeBypass ¶
RevokeBypass revokes RLS bypass from a role.
type PolicySQL ¶
type PolicySQL struct {
// contains filtered or unexported fields
}
PolicySQL generates SQL for creating an RLS policy.
func NewPolicySQL ¶
NewPolicySQL creates a new PolicySQL builder.
func (*PolicySQL) CreateAllPolicies ¶
CreateAllPolicies returns SQL to enable RLS and create all CRUD policies.
func (*PolicySQL) CreateDeletePolicy ¶
CreateDeletePolicy returns SQL to create a DELETE policy.
func (*PolicySQL) CreateInsertPolicy ¶
CreateInsertPolicy returns SQL to create an INSERT policy.
func (*PolicySQL) CreateSelectPolicy ¶
CreateSelectPolicy returns SQL to create a SELECT policy.
func (*PolicySQL) CreateUpdatePolicy ¶
CreateUpdatePolicy returns SQL to create an UPDATE policy.
func (*PolicySQL) DropAllPolicies ¶
DropAllPolicies returns SQL to drop all policies and disable RLS.
func (*PolicySQL) DropPolicy ¶
DropPolicy returns SQL to drop a policy.
type TableRLSStatus ¶
type TableRLSStatus struct {
Table string
RLSEnabled bool
RLSForced bool
HasPolicies bool
Policies []string
}
TableRLSStatus represents RLS status for a table.
type TestHelper ¶
type TestHelper struct {
// contains filtered or unexported fields
}
TestHelper provides testing utilities for RLS.
func NewTestHelper ¶
NewTestHelper creates a new RLS test helper.
func (*TestHelper) AsTenant ¶
AsTenant executes a test function with only tenant context (no specific user).
func (*TestHelper) AsUser ¶
AsUser executes a test function with RLS context set for a specific user and tenant.
Example:
th := rls.NewTestHelper(t, db, nil)
th.AsUser(tenantID, userID, func(ctx context.Context, tx *sql.Tx) {
// Queries here are scoped to tenantID
rows, err := tx.QueryContext(ctx, "SELECT * FROM items")
// ... assertions
})
func (*TestHelper) AssertCanRead ¶
func (th *TestHelper) AssertCanRead(tenantID, userID uuid.UUID, table, whereClause string, args ...any)
AssertCanRead verifies that a tenant can read specific data.
func (*TestHelper) AssertCannotRead ¶
func (th *TestHelper) AssertCannotRead(tenantID, userID uuid.UUID, table, whereClause string, args ...any)
AssertCannotRead verifies that a tenant cannot read specific data.
func (*TestHelper) AssertTenantIsolation ¶
func (th *TestHelper) AssertTenantIsolation(table, insertCol, valueCol string)
AssertTenantIsolation verifies that RLS properly isolates data between tenants. It inserts data as tenantA, then verifies tenantB cannot see it.
func (*TestHelper) SetupTestTenant ¶
func (th *TestHelper) SetupTestTenant(name string) (tenantID uuid.UUID, cleanup func())
SetupTestTenant creates a test tenant and returns cleanup function.
func (*TestHelper) SetupTestUser ¶
func (th *TestHelper) SetupTestUser(tenantID uuid.UUID, email, role string) (userID uuid.UUID, cleanup func())
SetupTestUser creates a test user in a tenant and returns cleanup function.
func (*TestHelper) WithoutRLS ¶
func (th *TestHelper) WithoutRLS(fn func(ctx context.Context, tx *sql.Tx))
WithoutRLS executes a test function without RLS context. Useful for setup/teardown or cross-tenant assertions.
type TxManager ¶
type TxManager struct {
// contains filtered or unexported fields
}
TxManager manages transactions with automatic RLS context.
func NewTxManager ¶
NewTxManager creates a new transaction manager.
func (*TxManager) Begin ¶
Begin starts a new transaction with RLS context set. The caller is responsible for committing or rolling back.
func (*TxManager) BeginFromContext ¶
BeginFromContext starts a transaction using context tenant/user.
func (*TxManager) WithTenant ¶
WithTenant executes a function within a tenant-scoped transaction.
type TxOptions ¶
type TxOptions struct {
// TenantID is the tenant to scope the transaction to.
TenantID uuid.UUID
// UserID is the user executing the transaction.
UserID uuid.UUID
// IsolationLevel sets the transaction isolation level.
IsolationLevel sql.IsolationLevel
// ReadOnly marks the transaction as read-only.
ReadOnly bool
}
TxOptions configures transaction behavior.