package security import ( "fmt" "net/http" "strconv" "strings" ) // This file provides example implementations of the required security callbacks. // Copy these functions and modify them to match your authentication and database schema. // ============================================================================= // EXAMPLE 1: Simple Header-Based Authentication // ============================================================================= // ExampleAuthenticateFromHeader extracts user ID from X-User-ID header func ExampleAuthenticateFromHeader(r *http.Request) (userID int, roles string, err error) { userIDStr := r.Header.Get("X-User-ID") if userIDStr == "" { return 0, "", fmt.Errorf("X-User-ID header not provided") } userID, err = strconv.Atoi(userIDStr) if err != nil { return 0, "", fmt.Errorf("invalid user ID format: %v", err) } // Optionally extract roles roles = r.Header.Get("X-User-Roles") // comma-separated: "admin,manager" return userID, roles, nil } // ============================================================================= // EXAMPLE 2: JWT Token Authentication // ============================================================================= // ExampleAuthenticateFromJWT parses a JWT token and extracts user info // You'll need to import a JWT library like github.com/golang-jwt/jwt/v5 func ExampleAuthenticateFromJWT(r *http.Request) (userID int, roles string, err error) { authHeader := r.Header.Get("Authorization") if authHeader == "" { return 0, "", fmt.Errorf("authorization header not provided") } // Extract Bearer token tokenString := strings.TrimPrefix(authHeader, "Bearer ") if tokenString == authHeader { return 0, "", fmt.Errorf("invalid authorization header format") } // TODO: Parse and validate JWT token // Example using github.com/golang-jwt/jwt/v5: // // token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { // return []byte(os.Getenv("JWT_SECRET")), nil // }) // // if err != nil || !token.Valid { // return 0, "", fmt.Errorf("invalid token: %v", err) // } // // claims := token.Claims.(jwt.MapClaims) // userID = int(claims["user_id"].(float64)) // roles = claims["roles"].(string) return 0, "", fmt.Errorf("JWT parsing not implemented - see example above") } // ============================================================================= // EXAMPLE 3: Session Cookie Authentication // ============================================================================= // ExampleAuthenticateFromSession validates a session cookie func ExampleAuthenticateFromSession(r *http.Request) (userID int, roles string, err error) { sessionCookie, err := r.Cookie("session_id") if err != nil { return 0, "", fmt.Errorf("session cookie not found") } // TODO: Validate session against your session store (Redis, database, etc.) // Example: // // session, err := sessionStore.Get(sessionCookie.Value) // if err != nil { // return 0, "", fmt.Errorf("invalid session") // } // // userID = session.UserID // roles = session.Roles _ = sessionCookie // Suppress unused warning until implemented return 0, "", fmt.Errorf("session validation not implemented - see example above") } // ============================================================================= // EXAMPLE 4: Column Security - Database Implementation // ============================================================================= // ExampleLoadColumnSecurityFromDatabase loads column security rules from database // This implementation assumes the following database schema: // // CREATE TABLE core.secacces ( // rid_secacces SERIAL PRIMARY KEY, // rid_hub INTEGER, // control TEXT, -- Format: "schema.table.column" // accesstype TEXT, -- "mask" or "hide" // jsonvalue JSONB -- Masking configuration // ); // // CREATE TABLE core.hub_link ( // rid_hub_parent INTEGER, -- Security group ID // rid_hub_child INTEGER, -- User ID // parent_hubtype TEXT -- 'secgroup' // ); func ExampleLoadColumnSecurityFromDatabase(pUserID int, pSchema, pTablename string) ([]ColumnSecurity, error) { colSecList := make([]ColumnSecurity, 0) // getExtraFilters := func(pStr string) map[string]string { // mp := make(map[string]string, 0) // for i, val := range strings.Split(pStr, ",") { // if i <= 1 { // continue // } // vals := strings.Split(val, ":") // if len(vals) > 1 { // mp[vals[0]] = vals[1] // } // } // return mp // } // rows, err := DBM.DBConn.Raw(fmt.Sprintf(` // SELECT a.rid_secacces, a.control, a.accesstype, a.jsonvalue // FROM core.secacces a // WHERE a.rid_hub IN ( // SELECT l.rid_hub_parent // FROM core.hub_link l // WHERE l.parent_hubtype = 'secgroup' // AND l.rid_hub_child = ? // ) // AND control ILIKE '%s.%s%%' // `, pSchema, pTablename), pUserID).Rows() // defer func() { // if rows != nil { // rows.Close() // } // }() // if err != nil { // return colSecList, fmt.Errorf("failed to fetch column security from SQL: %v", err) // } // for rows.Next() { // var rid int // var jsondata []byte // var control, accesstype string // err = rows.Scan(&rid, &control, &accesstype, &jsondata) // if err != nil { // return colSecList, fmt.Errorf("failed to scan column security: %v", err) // } // parts := strings.Split(control, ",") // ids := strings.Split(parts[0], ".") // if len(ids) < 3 { // continue // } // jsonvalue := make(map[string]interface{}) // if len(jsondata) > 1 { // err = json.Unmarshal(jsondata, &jsonvalue) // if err != nil { // logger.Error("Failed to parse json: %v", err) // } // } // colsec := ColumnSecurity{ // Schema: pSchema, // Tablename: pTablename, // UserID: pUserID, // Path: ids[2:], // ExtraFilters: getExtraFilters(control), // Accesstype: accesstype, // Control: control, // ID: int(rid), // } // // Parse masking configuration from JSON // if v, ok := jsonvalue["start"]; ok { // if value, ok := v.(float64); ok { // colsec.MaskStart = int(value) // } // } // if v, ok := jsonvalue["end"]; ok { // if value, ok := v.(float64); ok { // colsec.MaskEnd = int(value) // } // } // if v, ok := jsonvalue["invert"]; ok { // if value, ok := v.(bool); ok { // colsec.MaskInvert = value // } // } // if v, ok := jsonvalue["char"]; ok { // if value, ok := v.(string); ok { // colsec.MaskChar = value // } // } // colSecList = append(colSecList, colsec) // } return colSecList, nil } // ============================================================================= // EXAMPLE 5: Column Security - In-Memory/Static Configuration // ============================================================================= // ExampleLoadColumnSecurityFromConfig loads column security from static config func ExampleLoadColumnSecurityFromConfig(pUserID int, pSchema, pTablename string) ([]ColumnSecurity, error) { // Example: Define security rules in code or load from config file securityRules := map[string][]ColumnSecurity{ "public.employees": { { Schema: "public", Tablename: "employees", Path: []string{"ssn"}, Accesstype: "mask", MaskStart: 5, MaskEnd: 0, MaskChar: "*", }, { Schema: "public", Tablename: "employees", Path: []string{"salary"}, Accesstype: "hide", }, }, "public.customers": { { Schema: "public", Tablename: "customers", Path: []string{"credit_card"}, Accesstype: "mask", MaskStart: 12, MaskEnd: 0, MaskChar: "*", }, }, } key := fmt.Sprintf("%s.%s", pSchema, pTablename) rules, ok := securityRules[key] if !ok { return []ColumnSecurity{}, nil // No rules for this table } // Filter by user ID if needed // For this example, all rules apply to all users return rules, nil } // ============================================================================= // EXAMPLE 6: Row Security - Database Implementation // ============================================================================= // ExampleLoadRowSecurityFromDatabase loads row security rules from database // This implementation assumes a PostgreSQL function: // // CREATE FUNCTION core.api_sec_rowtemplate( // p_schema TEXT, // p_table TEXT, // p_userid INTEGER // ) RETURNS TABLE ( // p_retval INTEGER, // p_errmsg TEXT, // p_template TEXT, // p_block BOOLEAN // ); func ExampleLoadRowSecurityFromDatabase(pUserID int, pSchema, pTablename string) (RowSecurity, error) { record := RowSecurity{ Schema: pSchema, Tablename: pTablename, UserID: pUserID, } // rows, err := DBM.DBConn.Raw(` // SELECT r.p_retval, r.p_errmsg, r.p_template, r.p_block // FROM core.api_sec_rowtemplate(?, ?, ?) r // `, pSchema, pTablename, pUserID).Rows() // defer func() { // if rows != nil { // rows.Close() // } // }() // if err != nil { // return record, fmt.Errorf("failed to fetch row security from SQL: %v", err) // } // for rows.Next() { // var retval int // var errmsg string // err = rows.Scan(&retval, &errmsg, &record.Template, &record.HasBlock) // if err != nil { // return record, fmt.Errorf("failed to scan row security: %v", err) // } // if retval != 0 { // return RowSecurity{}, fmt.Errorf("api_sec_rowtemplate error: %s", errmsg) // } // } return record, nil } // ============================================================================= // EXAMPLE 7: Row Security - Static Configuration // ============================================================================= // ExampleLoadRowSecurityFromConfig loads row security from static config func ExampleLoadRowSecurityFromConfig(pUserID int, pSchema, pTablename string) (RowSecurity, error) { // Define row security templates based on entity templates := map[string]string{ "public.orders": "user_id = {UserID}", // Users see only their orders "public.documents": "user_id = {UserID} OR is_public = true", // Users see their docs + public docs "public.employees": "department_id IN (SELECT department_id FROM user_departments WHERE user_id = {UserID})", // Complex filter } // Define blocked entities (no access at all) blockedEntities := map[string][]int{ "public.admin_logs": {}, // All users blocked (empty list = block all) "public.audit_logs": {1, 2, 3}, // Block users 1, 2, 3 } key := fmt.Sprintf("%s.%s", pSchema, pTablename) // Check if entity is blocked for this user if blockedUsers, ok := blockedEntities[key]; ok { if len(blockedUsers) == 0 { // Block all users return RowSecurity{ Schema: pSchema, Tablename: pTablename, UserID: pUserID, HasBlock: true, }, nil } // Check if specific user is blocked for _, blockedUserID := range blockedUsers { if blockedUserID == pUserID { return RowSecurity{ Schema: pSchema, Tablename: pTablename, UserID: pUserID, HasBlock: true, }, nil } } } // Get template for this entity template, ok := templates[key] if !ok { // No row security defined - allow all rows return RowSecurity{ Schema: pSchema, Tablename: pTablename, UserID: pUserID, Template: "", HasBlock: false, }, nil } return RowSecurity{ Schema: pSchema, Tablename: pTablename, UserID: pUserID, Template: template, HasBlock: false, }, nil } // ============================================================================= // SETUP HELPER: Configure All Callbacks // ============================================================================= // SetupCallbacksExample shows how to configure all callbacks func SetupCallbacksExample() { // Option 1: Use database-backed security (production) GlobalSecurity.AuthenticateCallback = ExampleAuthenticateFromJWT GlobalSecurity.LoadColumnSecurityCallback = ExampleLoadColumnSecurityFromDatabase GlobalSecurity.LoadRowSecurityCallback = ExampleLoadRowSecurityFromDatabase // Option 2: Use static configuration (development/testing) // GlobalSecurity.AuthenticateCallback = ExampleAuthenticateFromHeader // GlobalSecurity.LoadColumnSecurityCallback = ExampleLoadColumnSecurityFromConfig // GlobalSecurity.LoadRowSecurityCallback = ExampleLoadRowSecurityFromConfig // Option 3: Mix and match // GlobalSecurity.AuthenticateCallback = ExampleAuthenticateFromJWT // GlobalSecurity.LoadColumnSecurityCallback = ExampleLoadColumnSecurityFromConfig // GlobalSecurity.LoadRowSecurityCallback = ExampleLoadRowSecurityFromDatabase }