rls

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package rls provides PostgreSQL Row-Level Security helpers for CoreForge.

Index

Constants

This section is empty.

Variables

View Source
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

func BypassRLS(roleName string) string

BypassRLS returns SQL to grant RLS bypass to a role. This is typically used for admin/migration roles.

func ContextWithTenant

func ContextWithTenant(ctx context.Context, tenantID uuid.UUID) context.Context

ContextWithTenant returns a new context with the tenant ID attached.

func ContextWithTenantAndUser

func ContextWithTenantAndUser(ctx context.Context, tenantID, userID uuid.UUID) context.Context

ContextWithTenantAndUser returns a new context with both tenant and user IDs.

func ContextWithUser

func ContextWithUser(ctx context.Context, userID uuid.UUID) context.Context

ContextWithUser returns a new context with the user ID attached.

func HasTenant

func HasTenant(ctx context.Context) bool

HasTenant checks if a tenant ID is present in the context.

func HasUser

func HasUser(ctx context.Context) bool

HasUser checks if a user ID is present in the context.

func NoBypassRLS

func NoBypassRLS(roleName string) string

NoBypassRLS returns SQL to revoke RLS bypass from a role.

func TenantIDFromContext

func TenantIDFromContext(ctx context.Context) uuid.UUID

TenantIDFromContext extracts the tenant ID from the context. Returns uuid.Nil if no tenant ID is present.

func TenantIDString

func TenantIDString(ctx context.Context) string

TenantIDString returns the tenant ID as a string, or empty string if not present.

func UserIDFromContext

func UserIDFromContext(ctx context.Context) uuid.UUID

UserIDFromContext extracts the user ID from the context. Returns uuid.Nil if no user ID is present.

func UserIDString

func UserIDString(ctx context.Context) string

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

func WithTenantFromContext(ctx context.Context, db *sql.DB, helper *Helper, fn TxFunc) error

WithTenantFromContext executes a function within a transaction using tenant and user IDs from the context.

func WithTenantOpts

func WithTenantOpts(ctx context.Context, db *sql.DB, helper *Helper, opts *TxOptions, fn TxFunc) error

WithTenantOpts executes a function within a transaction with RLS context and custom options.

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

type ConnWithRLS struct {
	*sql.Conn
	// contains filtered or unexported fields
}

ConnWithRLS wraps sql.Conn to ensure RLS context is set.

func GetConnWithRLS

func GetConnWithRLS(ctx context.Context, db *sql.DB, cfg *Config) (*ConnWithRLS, error)

GetConnWithRLS gets a connection from the pool with RLS context set.

func NewConnWithRLS

func NewConnWithRLS(ctx context.Context, conn *sql.Conn, cfg *Config) (*ConnWithRLS, error)

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

func (ci *ContextInjector) InjectContext(ctx context.Context, conn *sql.Conn) error

InjectContext sets RLS session variables on a connection. Use this with connection pool acquire hooks.

type DBWithRLS

type DBWithRLS struct {
	*sql.DB
	// contains filtered or unexported fields
}

DBWithRLS wraps a database connection to automatically set RLS context for each connection obtained from the pool.

func NewDBWithRLS

func NewDBWithRLS(db *sql.DB, cfg *Config) *DBWithRLS

NewDBWithRLS creates a new RLS-aware database wrapper.

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

func NewEntDriver(cfg *Config) *EntDriver

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

func NewEntHook(db *sql.DB, cfg *Config) *EntHook

NewEntHook creates a new Ent RLS hook.

func (*EntHook) SetContextFromContext

func (h *EntHook) SetContextFromContext(ctx context.Context) error

SetContextFromContext sets RLS context using tenant/user from Go context. This should be called at the start of each request/operation.

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 NewHelper

func NewHelper(cfg *Config) *Helper

NewHelper creates a new RLS helper with the given configuration.

func (*Helper) ClearContext

func (h *Helper) ClearContext(ctx context.Context, db Executor) error

ClearContext clears the tenant and user session variables.

func (*Helper) SetContext

func (h *Helper) SetContext(ctx context.Context, db Executor, tenantID, userID string) error

SetContext sets both tenant and user in the database session.

func (*Helper) SetTenant

func (h *Helper) SetTenant(ctx context.Context, db Executor, tenantID string) error

SetTenant sets the current tenant in the database session. This should be called at the start of each request/transaction.

func (*Helper) SetUser

func (h *Helper) SetUser(ctx context.Context, db Executor, userID string) error

SetUser sets the current 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

func (m *Migrator) DisableRLS(ctx context.Context, table string) error

DisableRLS disables RLS on a table and drops policies.

func (*Migrator) EnableRLS

func (m *Migrator) EnableRLS(ctx context.Context, table string) error

EnableRLS enables RLS on a table and creates standard policies.

func (*Migrator) GenerateMigrationSQL

func (m *Migrator) GenerateMigrationSQL() string

GenerateMigrationSQL generates SQL for RLS migration without executing it. Useful for reviewing or including in migration files.

func (*Migrator) GenerateRollbackSQL

func (m *Migrator) GenerateRollbackSQL() string

GenerateRollbackSQL generates SQL to undo RLS migration.

func (*Migrator) GrantBypass

func (m *Migrator) GrantBypass(ctx context.Context, role string) error

GrantBypass grants RLS bypass to a role.

func (*Migrator) MigrateCoreForge

func (m *Migrator) MigrateCoreForge(ctx context.Context) error

MigrateCoreForge applies RLS policies to CoreForge tables.

func (*Migrator) RevokeBypass

func (m *Migrator) RevokeBypass(ctx context.Context, role string) error

RevokeBypass revokes RLS bypass from a role.

func (*Migrator) VerifyRLS

func (m *Migrator) VerifyRLS(ctx context.Context) ([]TableRLSStatus, error)

VerifyRLS checks that RLS is properly configured on tables.

type PolicySQL

type PolicySQL struct {
	// contains filtered or unexported fields
}

PolicySQL generates SQL for creating an RLS policy.

func NewPolicySQL

func NewPolicySQL(tableName, policyName string, cfg *Config) *PolicySQL

NewPolicySQL creates a new PolicySQL builder.

func (*PolicySQL) CreateAllPolicies

func (p *PolicySQL) CreateAllPolicies() string

CreateAllPolicies returns SQL to enable RLS and create all CRUD policies.

func (*PolicySQL) CreateDeletePolicy

func (p *PolicySQL) CreateDeletePolicy() string

CreateDeletePolicy returns SQL to create a DELETE policy.

func (*PolicySQL) CreateInsertPolicy

func (p *PolicySQL) CreateInsertPolicy() string

CreateInsertPolicy returns SQL to create an INSERT policy.

func (*PolicySQL) CreateSelectPolicy

func (p *PolicySQL) CreateSelectPolicy() string

CreateSelectPolicy returns SQL to create a SELECT policy.

func (*PolicySQL) CreateUpdatePolicy

func (p *PolicySQL) CreateUpdatePolicy() string

CreateUpdatePolicy returns SQL to create an UPDATE policy.

func (*PolicySQL) DropAllPolicies

func (p *PolicySQL) DropAllPolicies() string

DropAllPolicies returns SQL to drop all policies and disable RLS.

func (*PolicySQL) DropPolicy

func (p *PolicySQL) DropPolicy(operation string) string

DropPolicy returns SQL to drop a policy.

func (*PolicySQL) EnableRLS

func (p *PolicySQL) EnableRLS() string

EnableRLS returns SQL to enable RLS on the table.

func (*PolicySQL) ForceRLS

func (p *PolicySQL) ForceRLS() string

ForceRLS returns SQL to force RLS even for table owners.

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

func NewTestHelper(t *testing.T, db *sql.DB, cfg *Config) *TestHelper

NewTestHelper creates a new RLS test helper.

func (*TestHelper) AsTenant

func (th *TestHelper) AsTenant(tenantID uuid.UUID, fn func(ctx context.Context, tx *sql.Tx))

AsTenant executes a test function with only tenant context (no specific user).

func (*TestHelper) AsUser

func (th *TestHelper) AsUser(tenantID, userID uuid.UUID, fn func(ctx context.Context, tx *sql.Tx))

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 TxFunc

type TxFunc func(tx *sql.Tx) error

TxFunc is a function that executes within a transaction.

type TxManager

type TxManager struct {
	// contains filtered or unexported fields
}

TxManager manages transactions with automatic RLS context.

func NewTxManager

func NewTxManager(db *sql.DB, cfg *Config) *TxManager

NewTxManager creates a new transaction manager.

func (*TxManager) Begin

func (m *TxManager) Begin(ctx context.Context, tenantID, userID uuid.UUID) (*sql.Tx, error)

Begin starts a new transaction with RLS context set. The caller is responsible for committing or rolling back.

func (*TxManager) BeginFromContext

func (m *TxManager) BeginFromContext(ctx context.Context) (*sql.Tx, error)

BeginFromContext starts a transaction using context tenant/user.

func (*TxManager) WithTenant

func (m *TxManager) WithTenant(ctx context.Context, tenantID, userID uuid.UUID, fn TxFunc) error

WithTenant executes a function within a tenant-scoped transaction.

func (*TxManager) WithTenantFromContext

func (m *TxManager) WithTenantFromContext(ctx context.Context, fn TxFunc) error

WithTenantFromContext executes a function using context tenant/user.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL