security

package
v0.0.63 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2025 License: MIT Imports: 17 Imported by: 0

README

ResolveSpec Security Provider

Type-safe, composable security system for ResolveSpec with support for authentication, column-level security (masking), and row-level security (filtering).

Features

  • Interface-Based - Type-safe providers instead of callbacks
  • Login/Logout Support - Built-in authentication lifecycle
  • Composable - Mix and match different providers
  • No Global State - Each handler has its own security configuration
  • Testable - Easy to mock and test
  • Extensible - Implement custom providers for your needs
  • Stored Procedures - All database operations use PostgreSQL stored procedures for security and maintainability

Stored Procedure Architecture

All database-backed security providers use PostgreSQL stored procedures exclusively. No raw SQL queries are executed from Go code.

Benefits
  • Security: Database logic is centralized and protected
  • Maintainability: Update database logic without recompiling Go code
  • Performance: Stored procedures are pre-compiled and optimized
  • Testability: Test database logic independently
  • Consistency: Standardized resolvespec_* naming convention
Available Stored Procedures
Procedure Purpose Used By
resolvespec_login Session-based login DatabaseAuthenticator
resolvespec_logout Session invalidation DatabaseAuthenticator
resolvespec_session Session validation DatabaseAuthenticator
resolvespec_session_update Update session activity DatabaseAuthenticator
resolvespec_refresh_token Token refresh DatabaseAuthenticator
resolvespec_jwt_login JWT user validation JWTAuthenticator
resolvespec_jwt_logout JWT token blacklist JWTAuthenticator
resolvespec_column_security Load column rules DatabaseColumnSecurityProvider
resolvespec_row_security Load row templates DatabaseRowSecurityProvider

See database_schema.sql for complete stored procedure definitions and examples.

Quick Start

import (
    "github.com/bitechdev/ResolveSpec/pkg/security"
    "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
)

// 1. Create security providers
auth := security.NewJWTAuthenticator("your-secret-key", db)
colSec := security.NewDatabaseColumnSecurityProvider(db)
rowSec := security.NewDatabaseRowSecurityProvider(db)

// 2. Combine providers
provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)

// 3. Setup security
handler := restheadspec.NewHandlerWithGORM(db)
securityList := security.SetupSecurityProvider(handler, provider)

// 4. Apply middleware
router := mux.NewRouter()
restheadspec.SetupMuxRoutes(router, handler)
router.Use(security.NewAuthMiddleware(securityList))
router.Use(security.SetSecurityMiddleware(securityList))

Architecture

Core Interfaces

The security system is built on three main interfaces:

1. Authenticator

Handles user authentication lifecycle:

type Authenticator interface {
    Login(ctx context.Context, req LoginRequest) (*LoginResponse, error)
    Logout(ctx context.Context, req LogoutRequest) error
    Authenticate(r *http.Request) (*UserContext, error)
}
2. ColumnSecurityProvider

Manages column-level security (masking/hiding):

type ColumnSecurityProvider interface {
    GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)
}
3. RowSecurityProvider

Manages row-level security (WHERE clause filtering):

type RowSecurityProvider interface {
    GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)
}
SecurityProvider

The main interface that combines all three:

type SecurityProvider interface {
    Authenticator
    ColumnSecurityProvider
    RowSecurityProvider
}
UserContext

Enhanced user context with complete user information:

type UserContext struct {
    UserID    int           // User's unique ID
    UserName  string        // Username
    UserLevel int           // User privilege level
    SessionID string        // Current session ID
    RemoteID  string        // Remote system ID
    Roles     []string      // User roles
    Email     string        // User email
    Claims    map[string]any // Additional metadata
}

Available Implementations

Authenticators

HeaderAuthenticator - Simple header-based authentication:

auth := security.NewHeaderAuthenticator()
// Expects: X-User-ID, X-User-Name, X-User-Level, etc.

DatabaseAuthenticator - Database session-based authentication (Recommended):

auth := security.NewDatabaseAuthenticator(db)
// Supports: Login, Logout, Session management, Token refresh
// All operations use stored procedures: resolvespec_login, resolvespec_logout,
// resolvespec_session, resolvespec_session_update, resolvespec_refresh_token
// Requires: users and user_sessions tables + stored procedures (see database_schema.sql)

JWTAuthenticator - JWT token authentication with login/logout:

auth := security.NewJWTAuthenticator("secret-key", db)
// Supports: Login, Logout, JWT token validation
// All operations use stored procedures: resolvespec_jwt_login, resolvespec_jwt_logout
// Note: Requires JWT library installation for token signing/verification
Column Security Providers

DatabaseColumnSecurityProvider - Loads rules from database:

colSec := security.NewDatabaseColumnSecurityProvider(db)
// Uses stored procedure: resolvespec_column_security
// Queries core.secaccess and core.hub_link tables

ConfigColumnSecurityProvider - Static configuration:

rules := map[string][]security.ColumnSecurity{
    "public.employees": {
        {Path: []string{"ssn"}, Accesstype: "mask", MaskStart: 5},
    },
}
colSec := security.NewConfigColumnSecurityProvider(rules)
Row Security Providers

DatabaseRowSecurityProvider - Loads filters from database:

rowSec := security.NewDatabaseRowSecurityProvider(db)
// Uses stored procedure: resolvespec_row_security

ConfigRowSecurityProvider - Static templates:

templates := map[string]string{
    "public.orders": "user_id = {UserID}",
}
blocked := map[string]bool{
    "public.admin_logs": true,
}
rowSec := security.NewConfigRowSecurityProvider(templates, blocked)

Usage Examples

Example 1: Complete Database-Backed Security with Sessions
func main() {
    db := setupDatabase()

    // Run migrations (see database_schema.sql)
    // db.Exec("CREATE TABLE users ...")
    // db.Exec("CREATE TABLE user_sessions ...")

    handler := restheadspec.NewHandlerWithGORM(db)

    // Create providers
    auth := security.NewDatabaseAuthenticator(db) // Session-based auth
    colSec := security.NewDatabaseColumnSecurityProvider(db)
    rowSec := security.NewDatabaseRowSecurityProvider(db)

    // Combine
    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.SetupSecurityProvider(handler, provider)

    // Setup routes
    router := mux.NewRouter()

    // Add auth endpoints
    router.HandleFunc("/auth/login", handleLogin(securityList)).Methods("POST")
    router.HandleFunc("/auth/logout", handleLogout(securityList)).Methods("POST")
    router.HandleFunc("/auth/refresh", handleRefresh(securityList)).Methods("POST")

    // Setup API with security
    apiRouter := router.PathPrefix("/api").Subrouter()
    restheadspec.SetupMuxRoutes(apiRouter, handler)
    apiRouter.Use(security.NewAuthMiddleware(securityList))
    apiRouter.Use(security.SetSecurityMiddleware(securityList))

    http.ListenAndServe(":8080", router)
}

func handleLogin(securityList *security.SecurityList) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var req security.LoginRequest
        json.NewDecoder(r.Body).Decode(&req)

        // Add client info to claims
        req.Claims = map[string]any{
            "ip_address": r.RemoteAddr,
            "user_agent": r.UserAgent(),
        }

        resp, err := securityList.Provider().Login(r.Context(), req)
        if err != nil {
            http.Error(w, err.Error(), http.StatusUnauthorized)
            return
        }

        // Set session cookie (optional)
        http.SetCookie(w, &http.Cookie{
            Name:     "session_token",
            Value:    resp.Token,
            Expires:  time.Now().Add(24 * time.Hour),
            HttpOnly: true,
            Secure:   true, // Use in production with HTTPS
            SameSite: http.SameSiteStrictMode,
        })

        json.NewEncoder(w).Encode(resp)
    }
}

func handleRefresh(securityList *security.SecurityList) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("X-Refresh-Token")

        if refreshable, ok := securityList.Provider().(security.Refreshable); ok {
            resp, err := refreshable.RefreshToken(r.Context(), token)
            if err != nil {
                http.Error(w, err.Error(), http.StatusUnauthorized)
                return
            }
            json.NewEncoder(w).Encode(resp)
        } else {
            http.Error(w, "Refresh not supported", http.StatusNotImplemented)
        }
    }
}
Example 2: Config-Based Security (No Database)
func main() {
    db := setupDatabase()
    handler := restheadspec.NewHandlerWithGORM(db)

    // Static column security rules
    columnRules := map[string][]security.ColumnSecurity{
        "public.employees": {
            {Path: []string{"ssn"}, Accesstype: "mask", MaskStart: 5},
            {Path: []string{"salary"}, Accesstype: "hide"},
        },
    }

    // Static row security templates
    rowTemplates := map[string]string{
        "public.orders": "user_id = {UserID}",
    }

    // Create providers
    auth := security.NewHeaderAuthenticator()
    colSec := security.NewConfigColumnSecurityProvider(columnRules)
    rowSec := security.NewConfigRowSecurityProvider(rowTemplates, nil)

    provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)
    securityList := security.SetupSecurityProvider(handler, provider)

    // Setup routes...
}
Example 3: Custom Provider

Implement your own provider for complete control:

type MySecurityProvider struct {
    db *gorm.DB
}

func (p *MySecurityProvider) Login(ctx context.Context, req security.LoginRequest) (*security.LoginResponse, error) {
    // Your custom login logic
}

func (p *MySecurityProvider) Logout(ctx context.Context, req security.LogoutRequest) error {
    // Your custom logout logic
}

func (p *MySecurityProvider) Authenticate(r *http.Request) (*security.UserContext, error) {
    // Your custom authentication logic
}

func (p *MySecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    // Your custom column security logic
}

func (p *MySecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    // Your custom row security logic
}

// Use it
provider := &MySecurityProvider{db: db}
securityList := security.SetupSecurityProvider(handler, provider)

Security Features

Column Security (Masking/Hiding)

Mask SSN (show last 4 digits):

{
    Path:       []string{"ssn"},
    Accesstype: "mask",
    MaskStart:  5,
    MaskChar:   "*",
}
// "123-45-6789" → "*****6789"

Hide entire field:

{
    Path:       []string{"salary"},
    Accesstype: "hide",
}
// Field returns 0 or empty

Nested JSON field masking:

{
    Path:       []string{"address", "street"},
    Accesstype: "mask",
    MaskStart:  10,
}
Row Security (Filtering)

User isolation:

{
    Template: "user_id = {UserID}",
}
// Users only see their own records

Tenant isolation:

{
    Template: "tenant_id = {TenantID} AND user_id = {UserID}",
}

Block all access:

{
    HasBlock: true,
}
// Completely blocks access to the table

Template variables:

  • {UserID} - Current user's ID
  • {PrimaryKeyName} - Primary key column
  • {TableName} - Table name
  • {SchemaName} - Schema name

Request Flow

HTTP Request
    ↓
NewAuthMiddleware
    ├─ Calls provider.Authenticate(request)
    └─ Adds UserContext to context
    ↓
SetSecurityMiddleware
    └─ Adds SecurityList to context
    ↓
Handler.Handle()
    ↓
BeforeRead Hook
    ├─ Calls provider.GetColumnSecurity()
    └─ Calls provider.GetRowSecurity()
    ↓
BeforeScan Hook
    └─ Applies row security (adds WHERE clause)
    ↓
Database Query (with security filters)
    ↓
AfterRead Hook
    └─ Applies column security (masks/hides fields)
    ↓
HTTP Response (secured data)

Testing

The interface-based design makes testing straightforward:

// Mock authenticator for tests
type MockAuthenticator struct {
    UserToReturn *security.UserContext
    ErrorToReturn error
}

func (m *MockAuthenticator) Authenticate(r *http.Request) (*security.UserContext, error) {
    return m.UserToReturn, m.ErrorToReturn
}

// Use in tests
func TestMyHandler(t *testing.T) {
    mockAuth := &MockAuthenticator{
        UserToReturn: &security.UserContext{UserID: 123},
    }

    provider := security.NewCompositeSecurityProvider(
        mockAuth,
        &MockColumnSecurity{},
        &MockRowSecurity{},
    )

    securityList := security.SetupSecurityProvider(handler, provider)
    // ... test your handler
}

Migration from Callbacks

If you're upgrading from the old callback-based system:

Old:

security.GlobalSecurity.AuthenticateCallback = myAuthFunc
security.GlobalSecurity.LoadColumnSecurityCallback = myColSecFunc
security.GlobalSecurity.LoadRowSecurityCallback = myRowSecFunc
security.SetupSecurityProvider(handler, &security.GlobalSecurity)

New:

// Wrap your functions in a provider
type MyProvider struct{}

func (p *MyProvider) Authenticate(r *http.Request) (*security.UserContext, error) {
    userID, roles, err := myAuthFunc(r)
    return &security.UserContext{UserID: userID, Roles: strings.Split(roles, ",")}, err
}

func (p *MyProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    return myColSecFunc(userID, schema, table)
}

func (p *MyProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    return myRowSecFunc(userID, schema, table)
}

func (p *MyProvider) Login(ctx context.Context, req security.LoginRequest) (*security.LoginResponse, error) {
    return nil, fmt.Errorf("not implemented")
}

func (p *MyProvider) Logout(ctx context.Context, req security.LogoutRequest) error {
    return nil
}

// Use it
provider := &MyProvider{}
securityList := security.SetupSecurityProvider(handler, provider)

Documentation

File Description
QUICK_REFERENCE.md Quick reference guide with examples
INTERFACE_GUIDE.md Complete implementation guide
examples.go Working provider implementations
setup_example.go 6 complete integration examples

API Reference

Context Helpers

Get user information from request context:

userCtx, ok := security.GetUserContext(ctx)
userID, ok := security.GetUserID(ctx)
userName, ok := security.GetUserName(ctx)
userLevel, ok := security.GetUserLevel(ctx)
sessionID, ok := security.GetSessionID(ctx)
remoteID, ok := security.GetRemoteID(ctx)
roles, ok := security.GetUserRoles(ctx)
email, ok := security.GetUserEmail(ctx)
Optional Interfaces

Implement these for additional features:

Refreshable - Token refresh support:

type Refreshable interface {
    RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)
}

Validatable - Token validation:

type Validatable interface {
    ValidateToken(ctx context.Context, token string) (bool, error)
}

Cacheable - Cache management:

type Cacheable interface {
    ClearCache(ctx context.Context, userID int, schema, table string) error
}

Benefits Over Callbacks

Feature Old (Callbacks) New (Interfaces)
Type Safety ❌ Callbacks can be nil ✅ Compile-time verification
Global State ❌ GlobalSecurity variable ✅ Dependency injection
Testability ⚠️ Need to set globals ✅ Easy to mock
Composability ❌ Single provider only ✅ Mix and match
Login/Logout ❌ Not supported ✅ Built-in
Extensibility ⚠️ Limited ✅ Optional interfaces

Common Patterns

Caching Security Rules
type CachedProvider struct {
    inner security.ColumnSecurityProvider
    cache *cache.Cache
}

func (p *CachedProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    key := fmt.Sprintf("%d:%s.%s", userID, schema, table)
    if cached, found := p.cache.Get(key); found {
        return cached.([]security.ColumnSecurity), nil
    }

    rules, err := p.inner.GetColumnSecurity(ctx, userID, schema, table)
    if err == nil {
        p.cache.Set(key, rules, cache.DefaultExpiration)
    }
    return rules, err
}
Role-Based Security
func (p *MyProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]security.ColumnSecurity, error) {
    userCtx, _ := security.GetUserContext(ctx)

    if contains(userCtx.Roles, "admin") {
        return []security.ColumnSecurity{}, nil // No restrictions
    }

    return loadRestrictionsForUser(userID, schema, table), nil
}
Multi-Tenant Isolation
func (p *MyProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (security.RowSecurity, error) {
    tenantID := getUserTenant(userID)

    return security.RowSecurity{
        Template: fmt.Sprintf("tenant_id = %d AND user_id = {UserID}", tenantID),
    }, nil
}

License

Part of the ResolveSpec project.

Documentation

Index

Constants

View Source
const (
	// Context keys for user information
	UserIDKey      contextKey = "user_id"
	UserNameKey    contextKey = "user_name"
	UserLevelKey   contextKey = "user_level"
	SessionIDKey   contextKey = "session_id"
	RemoteIDKey    contextKey = "remote_id"
	UserRolesKey   contextKey = "user_roles"
	UserEmailKey   contextKey = "user_email"
	UserContextKey contextKey = "user_context"
)

Variables

This section is empty.

Functions

func ApplyColumnSecurity added in v0.0.63

func ApplyColumnSecurity(hookCtx *restheadspec.HookContext, securityList *SecurityList) error

ApplyColumnSecurity applies column-level security (masking/hiding) to results

func ApplyRowSecurity added in v0.0.63

func ApplyRowSecurity(hookCtx *restheadspec.HookContext, securityList *SecurityList) error

ApplyRowSecurity applies row-level security filters to the query

func CompleteServerExample added in v0.0.63

func CompleteServerExample(gormDB interface{}, sqlDB *sql.DB) http.Handler

func ExampleConfigSecurity added in v0.0.63

func ExampleConfigSecurity(gormDB interface{}) (*mux.Router, error)

func ExampleDatabaseSecurity added in v0.0.63

func ExampleDatabaseSecurity(gormDB interface{}, sqlDB *sql.DB) (http.Handler, error)

func ExampleHeaderAuthentication added in v0.0.63

func ExampleHeaderAuthentication(gormDB interface{}, sqlDB *sql.DB) (*mux.Router, error)

func GetRemoteID added in v0.0.63

func GetRemoteID(ctx context.Context) (string, bool)

GetRemoteID extracts the remote ID from context

func GetSessionID added in v0.0.63

func GetSessionID(ctx context.Context) (string, bool)

GetSessionID extracts the session ID from context

func GetUserEmail added in v0.0.63

func GetUserEmail(ctx context.Context) (string, bool)

GetUserEmail extracts user email from context

func GetUserID

func GetUserID(ctx context.Context) (int, bool)

GetUserID extracts the user ID from context

func GetUserLevel added in v0.0.63

func GetUserLevel(ctx context.Context) (int, bool)

GetUserLevel extracts the user level from context

func GetUserName added in v0.0.63

func GetUserName(ctx context.Context) (string, bool)

GetUserName extracts the user name from context

func GetUserRoles

func GetUserRoles(ctx context.Context) ([]string, bool)

GetUserRoles extracts user roles from context

func LoadSecurityRules added in v0.0.63

func LoadSecurityRules(hookCtx *restheadspec.HookContext, securityList *SecurityList) error

LoadSecurityRules loads security configuration for the user and entity

func LogDataAccess added in v0.0.63

func LogDataAccess(hookCtx *restheadspec.HookContext) error

LogDataAccess logs all data access for audit purposes

func NewAuthMiddleware added in v0.0.63

func NewAuthMiddleware(securityList *SecurityList) func(http.Handler) http.Handler

NewAuthMiddleware creates an authentication middleware with the given security list This middleware extracts user authentication from the request and adds it to context

func RegisterSecurityHooks

func RegisterSecurityHooks(handler *restheadspec.Handler, securityList *SecurityList)

RegisterSecurityHooks registers all security-related hooks with the handler

func SetSecurityMiddleware

func SetSecurityMiddleware(securityList *SecurityList) func(http.Handler) http.Handler

SetSecurityMiddleware adds security context to requests This middleware should be applied after AuthMiddleware

func SetupAuthRoutes added in v0.0.63

func SetupAuthRoutes(router *mux.Router, securityList *SecurityList)

Types

type Authenticator added in v0.0.63

type Authenticator interface {
	// Login authenticates credentials and returns a token
	Login(ctx context.Context, req LoginRequest) (*LoginResponse, error)

	// Logout invalidates a user's session/token
	Logout(ctx context.Context, req LogoutRequest) error

	// Authenticate extracts and validates user from HTTP request
	// Returns UserContext or error if authentication fails
	Authenticate(r *http.Request) (*UserContext, error)
}

Authenticator handles user authentication operations

type CONTEXT_KEY added in v0.0.20

type CONTEXT_KEY string
const SECURITY_CONTEXT_KEY CONTEXT_KEY = "SecurityList"

type Cacheable added in v0.0.63

type Cacheable interface {
	// ClearCache clears cached security rules for a user/entity
	ClearCache(ctx context.Context, userID int, schema, table string) error
}

Cacheable allows providers to support caching of security rules

type ColumnSecurity

type ColumnSecurity struct {
	Schema       string
	Tablename    string
	Path         []string
	ExtraFilters map[string]string
	UserID       int
	Accesstype   string `json:"accesstype"`
	MaskStart    int
	MaskEnd      int
	MaskInvert   bool
	MaskChar     string
	Control      string `json:"control"`
	ID           int    `json:"id"`
}

type ColumnSecurityProvider added in v0.0.63

type ColumnSecurityProvider interface {
	// GetColumnSecurity loads column security rules for a user and entity
	GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)
}

ColumnSecurityProvider handles column-level security (masking/hiding)

type CompositeSecurityProvider added in v0.0.63

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

CompositeSecurityProvider combines multiple security providers Allows separating authentication, column security, and row security concerns

func NewCompositeSecurityProvider added in v0.0.63

func NewCompositeSecurityProvider(
	auth Authenticator,
	colSec ColumnSecurityProvider,
	rowSec RowSecurityProvider,
) *CompositeSecurityProvider

NewCompositeSecurityProvider creates a composite provider All parameters are required

func (*CompositeSecurityProvider) Authenticate added in v0.0.63

func (c *CompositeSecurityProvider) Authenticate(r *http.Request) (*UserContext, error)

Authenticate delegates to the authenticator

func (*CompositeSecurityProvider) ClearCache added in v0.0.63

func (c *CompositeSecurityProvider) ClearCache(ctx context.Context, userID int, schema, table string) error

ClearCache implements Cacheable if any provider supports it

func (*CompositeSecurityProvider) GetColumnSecurity added in v0.0.63

func (c *CompositeSecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)

GetColumnSecurity delegates to the column security provider

func (*CompositeSecurityProvider) GetRowSecurity added in v0.0.63

func (c *CompositeSecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)

GetRowSecurity delegates to the row security provider

func (*CompositeSecurityProvider) Login added in v0.0.63

Login delegates to the authenticator

func (*CompositeSecurityProvider) Logout added in v0.0.63

Logout delegates to the authenticator

func (*CompositeSecurityProvider) RefreshToken added in v0.0.63

func (c *CompositeSecurityProvider) RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)

RefreshToken implements Refreshable if the authenticator supports it

func (*CompositeSecurityProvider) ValidateToken added in v0.0.63

func (c *CompositeSecurityProvider) ValidateToken(ctx context.Context, token string) (bool, error)

ValidateToken implements Validatable if the authenticator supports it

type ConfigColumnSecurityProvider added in v0.0.63

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

ConfigColumnSecurityProvider provides static column security configuration

func NewConfigColumnSecurityProvider added in v0.0.63

func NewConfigColumnSecurityProvider(rules map[string][]ColumnSecurity) *ConfigColumnSecurityProvider

func (*ConfigColumnSecurityProvider) GetColumnSecurity added in v0.0.63

func (p *ConfigColumnSecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)

type ConfigRowSecurityProvider added in v0.0.63

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

ConfigRowSecurityProvider provides static row security configuration

func NewConfigRowSecurityProvider added in v0.0.63

func NewConfigRowSecurityProvider(templates map[string]string, blocked map[string]bool) *ConfigRowSecurityProvider

func (*ConfigRowSecurityProvider) GetRowSecurity added in v0.0.63

func (p *ConfigRowSecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)

type CustomSecurityProvider added in v0.0.63

type CustomSecurityProvider struct {
}

You can implement your own SecurityProvider by implementing all three interfaces

func (*CustomSecurityProvider) Authenticate added in v0.0.63

func (p *CustomSecurityProvider) Authenticate(r *http.Request) (*UserContext, error)

func (*CustomSecurityProvider) GetColumnSecurity added in v0.0.63

func (p *CustomSecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)

func (*CustomSecurityProvider) GetRowSecurity added in v0.0.63

func (p *CustomSecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)

func (*CustomSecurityProvider) Login added in v0.0.63

func (*CustomSecurityProvider) Logout added in v0.0.63

type DatabaseAuthenticator added in v0.0.63

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

DatabaseAuthenticator provides session-based authentication with database storage All database operations go through stored procedures for security and consistency Requires stored procedures: resolvespec_login, resolvespec_logout, resolvespec_session, resolvespec_session_update, resolvespec_refresh_token See database_schema.sql for procedure definitions

func NewDatabaseAuthenticator added in v0.0.63

func NewDatabaseAuthenticator(db *sql.DB) *DatabaseAuthenticator

func (*DatabaseAuthenticator) Authenticate added in v0.0.63

func (a *DatabaseAuthenticator) Authenticate(r *http.Request) (*UserContext, error)

func (*DatabaseAuthenticator) Login added in v0.0.63

func (*DatabaseAuthenticator) Logout added in v0.0.63

func (*DatabaseAuthenticator) RefreshToken added in v0.0.63

func (a *DatabaseAuthenticator) RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)

RefreshToken implements Refreshable interface

type DatabaseAuthenticatorExample added in v0.0.63

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

func NewDatabaseAuthenticatorExample added in v0.0.63

func NewDatabaseAuthenticatorExample(db *gorm.DB) *DatabaseAuthenticatorExample

func (*DatabaseAuthenticatorExample) Authenticate added in v0.0.63

func (a *DatabaseAuthenticatorExample) Authenticate(r *http.Request) (*UserContext, error)

func (*DatabaseAuthenticatorExample) Login added in v0.0.63

func (*DatabaseAuthenticatorExample) Logout added in v0.0.63

func (*DatabaseAuthenticatorExample) RefreshToken added in v0.0.63

func (a *DatabaseAuthenticatorExample) RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)

Optional: Implement Refreshable interface

type DatabaseColumnSecurityProvider added in v0.0.63

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

DatabaseColumnSecurityProvider loads column security from database All database operations go through stored procedures Requires stored procedure: resolvespec_column_security

func NewDatabaseColumnSecurityProvider added in v0.0.63

func NewDatabaseColumnSecurityProvider(db *sql.DB) *DatabaseColumnSecurityProvider

func (*DatabaseColumnSecurityProvider) GetColumnSecurity added in v0.0.63

func (p *DatabaseColumnSecurityProvider) GetColumnSecurity(ctx context.Context, userID int, schema, table string) ([]ColumnSecurity, error)

type DatabaseRowSecurityProvider added in v0.0.63

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

DatabaseRowSecurityProvider loads row security from database All database operations go through stored procedures Requires stored procedure: resolvespec_row_security

func NewDatabaseRowSecurityProvider added in v0.0.63

func NewDatabaseRowSecurityProvider(db *sql.DB) *DatabaseRowSecurityProvider

func (*DatabaseRowSecurityProvider) GetRowSecurity added in v0.0.63

func (p *DatabaseRowSecurityProvider) GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)

type HeaderAuthenticator added in v0.0.63

type HeaderAuthenticator struct{}

HeaderAuthenticator provides simple header-based authentication Expects: X-User-ID, X-User-Name, X-User-Level, X-Session-ID, X-Remote-ID, X-User-Roles, X-User-Email

func NewHeaderAuthenticator added in v0.0.63

func NewHeaderAuthenticator() *HeaderAuthenticator

func (*HeaderAuthenticator) Authenticate added in v0.0.63

func (a *HeaderAuthenticator) Authenticate(r *http.Request) (*UserContext, error)

func (*HeaderAuthenticator) Login added in v0.0.63

func (*HeaderAuthenticator) Logout added in v0.0.63

type HeaderAuthenticatorExample added in v0.0.63

type HeaderAuthenticatorExample struct {
}

func NewHeaderAuthenticatorExample added in v0.0.63

func NewHeaderAuthenticatorExample() *HeaderAuthenticatorExample

func (*HeaderAuthenticatorExample) Authenticate added in v0.0.63

func (a *HeaderAuthenticatorExample) Authenticate(r *http.Request) (*UserContext, error)

func (*HeaderAuthenticatorExample) Login added in v0.0.63

func (*HeaderAuthenticatorExample) Logout added in v0.0.63

type JWTAuthenticator added in v0.0.63

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

JWTAuthenticator provides JWT token-based authentication All database operations go through stored procedures Requires stored procedures: resolvespec_jwt_login, resolvespec_jwt_logout NOTE: JWT signing/verification requires github.com/golang-jwt/jwt/v5 to be installed and imported

func NewJWTAuthenticator added in v0.0.63

func NewJWTAuthenticator(secretKey string, db *sql.DB) *JWTAuthenticator

func (*JWTAuthenticator) Authenticate added in v0.0.63

func (a *JWTAuthenticator) Authenticate(r *http.Request) (*UserContext, error)

func (*JWTAuthenticator) Login added in v0.0.63

func (*JWTAuthenticator) Logout added in v0.0.63

func (a *JWTAuthenticator) Logout(ctx context.Context, req LogoutRequest) error

type JWTAuthenticatorExample added in v0.0.63

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

func NewJWTAuthenticatorExample added in v0.0.63

func NewJWTAuthenticatorExample(secretKey string, db *gorm.DB) *JWTAuthenticatorExample

func (*JWTAuthenticatorExample) Authenticate added in v0.0.63

func (a *JWTAuthenticatorExample) Authenticate(r *http.Request) (*UserContext, error)

func (*JWTAuthenticatorExample) Login added in v0.0.63

func (*JWTAuthenticatorExample) Logout added in v0.0.63

type LoginRequest added in v0.0.63

type LoginRequest struct {
	Username string
	Password string
	Claims   map[string]any // Additional login data
}

LoginRequest contains credentials for login

type LoginResponse added in v0.0.63

type LoginResponse struct {
	Token        string
	RefreshToken string
	User         *UserContext
	ExpiresIn    int64 // Token expiration in seconds
}

LoginResponse contains the result of a login attempt

type LogoutRequest added in v0.0.63

type LogoutRequest struct {
	Token  string
	UserID int
}

LogoutRequest contains information for logout

type Refreshable added in v0.0.63

type Refreshable interface {
	// RefreshToken exchanges a refresh token for a new access token
	RefreshToken(ctx context.Context, refreshToken string) (*LoginResponse, error)
}

Refreshable allows providers to support token refresh

type RowSecurity

type RowSecurity struct {
	Schema    string
	Tablename string
	Template  string
	HasBlock  bool
	UserID    int
}

func (*RowSecurity) GetTemplate

func (m *RowSecurity) GetTemplate(pPrimaryKeyName string, pModelType reflect.Type) string

type RowSecurityProvider added in v0.0.63

type RowSecurityProvider interface {
	// GetRowSecurity loads row security rules for a user and entity
	GetRowSecurity(ctx context.Context, userID int, schema, table string) (RowSecurity, error)
}

RowSecurityProvider handles row-level security (filtering)

type SecurityList

type SecurityList struct {
	ColumnSecurityMutex sync.RWMutex
	ColumnSecurity      map[string][]ColumnSecurity
	RowSecurityMutex    sync.RWMutex
	RowSecurity         map[string]RowSecurity
	// contains filtered or unexported fields
}

SecurityList manages security state and caching It wraps a SecurityProvider and provides caching and utility methods

func NewSecurityList added in v0.0.63

func NewSecurityList(provider SecurityProvider) *SecurityList

NewSecurityList creates a new security list with the given provider

func SetupSecurityProvider

func SetupSecurityProvider(handler *restheadspec.Handler, provider SecurityProvider) *SecurityList

SetupSecurityProvider initializes and configures the security provider This function creates a SecurityList with the given provider and registers hooks

Example usage:

// Create your security provider (use composite or single provider)
auth := security.NewJWTAuthenticator("your-secret-key", db)
colSec := security.NewDatabaseColumnSecurityProvider(db)
rowSec := security.NewDatabaseRowSecurityProvider(db)
provider := security.NewCompositeSecurityProvider(auth, colSec, rowSec)

// Setup security with the provider
handler := restheadspec.NewHandlerWithGORM(db)
securityList := security.SetupSecurityProvider(handler, provider)

// Apply middleware
router.Use(security.NewAuthMiddleware(securityList))
router.Use(security.SetSecurityMiddleware(securityList))

func (*SecurityList) ApplyColumnSecurity

func (m *SecurityList) ApplyColumnSecurity(records reflect.Value, modelType reflect.Type, pUserID int, pSchema, pTablename string) (reflect.Value, error)

func (*SecurityList) ClearSecurity

func (m *SecurityList) ClearSecurity(pUserID int, pSchema, pTablename string) error

func (*SecurityList) ColumSecurityApplyOnRecord

func (m *SecurityList) ColumSecurityApplyOnRecord(prevRecord reflect.Value, newRecord reflect.Value, modelType reflect.Type, pUserID int, pSchema, pTablename string) ([]string, error)

func (*SecurityList) GetRowSecurityTemplate

func (m *SecurityList) GetRowSecurityTemplate(pUserID int, pSchema, pTablename string) (RowSecurity, error)

func (*SecurityList) LoadColumnSecurity

func (m *SecurityList) LoadColumnSecurity(ctx context.Context, pUserID int, pSchema, pTablename string, pOverwrite bool) error

func (*SecurityList) LoadRowSecurity

func (m *SecurityList) LoadRowSecurity(ctx context.Context, pUserID int, pSchema, pTablename string, pOverwrite bool) (RowSecurity, error)

func (*SecurityList) Provider added in v0.0.63

func (m *SecurityList) Provider() SecurityProvider

Provider returns the underlying security provider

type SecurityProvider added in v0.0.63

type SecurityProvider interface {
	Authenticator
	ColumnSecurityProvider
	RowSecurityProvider
}

SecurityProvider is the main interface combining all security concerns

type UserContext added in v0.0.63

type UserContext struct {
	UserID    int
	UserName  string
	UserLevel int
	SessionID string
	RemoteID  string
	Roles     []string
	Email     string
	Claims    map[string]any
}

UserContext holds authenticated user information

func GetUserContext added in v0.0.63

func GetUserContext(ctx context.Context) (*UserContext, bool)

GetUserContext extracts the full user context from request context

type Validatable added in v0.0.63

type Validatable interface {
	// ValidateToken checks if a token is valid without extracting full user context
	ValidateToken(ctx context.Context, token string) (bool, error)
}

Validatable allows providers to validate tokens without full authentication

Jump to

Keyboard shortcuts

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