mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-03-07 05:58:55 +00:00
- Implement BeforeHandle hook to enforce authentication based on model rules. - Integrate with existing security mechanisms to allow or deny access. - Update documentation to reflect new hook and its usage.
108 lines
3.4 KiB
Go
108 lines
3.4 KiB
Go
package funcspec
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/security"
|
|
)
|
|
|
|
// RegisterSecurityHooks registers security hooks for funcspec handlers
|
|
// Note: funcspec operates on SQL queries directly, so row-level security is not directly applicable
|
|
// We provide auth enforcement and audit logging for data access tracking
|
|
func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList) {
|
|
// Hook 0: BeforeQueryList - Auth check before list query execution
|
|
handler.Hooks().Register(BeforeQueryList, func(hookCtx *HookContext) error {
|
|
if hookCtx.UserContext == nil || hookCtx.UserContext.UserID == 0 {
|
|
hookCtx.Abort = true
|
|
hookCtx.AbortMessage = "authentication required"
|
|
hookCtx.AbortCode = http.StatusUnauthorized
|
|
return fmt.Errorf("authentication required")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
// Hook 0: BeforeQuery - Auth check before single query execution
|
|
handler.Hooks().Register(BeforeQuery, func(hookCtx *HookContext) error {
|
|
if hookCtx.UserContext == nil || hookCtx.UserContext.UserID == 0 {
|
|
hookCtx.Abort = true
|
|
hookCtx.AbortMessage = "authentication required"
|
|
hookCtx.AbortCode = http.StatusUnauthorized
|
|
return fmt.Errorf("authentication required")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
// Hook 1: BeforeQueryList - Audit logging before query list execution
|
|
handler.Hooks().Register(BeforeQueryList, func(hookCtx *HookContext) error {
|
|
secCtx := newFuncSpecSecurityContext(hookCtx)
|
|
return security.LogDataAccess(secCtx)
|
|
})
|
|
|
|
// Hook 2: BeforeQuery - Audit logging before single query execution
|
|
handler.Hooks().Register(BeforeQuery, func(hookCtx *HookContext) error {
|
|
secCtx := newFuncSpecSecurityContext(hookCtx)
|
|
return security.LogDataAccess(secCtx)
|
|
})
|
|
|
|
// Note: Row-level security and column masking are challenging in funcspec
|
|
// because the SQL query is fully user-defined. Security should be implemented
|
|
// at the SQL function level or through database policies (RLS).
|
|
}
|
|
|
|
// funcSpecSecurityContext adapts funcspec.HookContext to security.SecurityContext interface
|
|
type funcSpecSecurityContext struct {
|
|
ctx *HookContext
|
|
}
|
|
|
|
func newFuncSpecSecurityContext(ctx *HookContext) security.SecurityContext {
|
|
return &funcSpecSecurityContext{ctx: ctx}
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetContext() context.Context {
|
|
return f.ctx.Context
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetUserID() (int, bool) {
|
|
if f.ctx.UserContext == nil {
|
|
return 0, false
|
|
}
|
|
return int(f.ctx.UserContext.UserID), true
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetSchema() string {
|
|
// funcspec doesn't have a schema concept, extract from SQL query or use default
|
|
return "public"
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetEntity() string {
|
|
// funcspec doesn't have an entity concept, could parse from SQL or use a placeholder
|
|
return "sql_query"
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetModel() interface{} {
|
|
// funcspec doesn't use models in the same way as restheadspec
|
|
return nil
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetQuery() interface{} {
|
|
// In funcspec, the query is a string, not a query builder object
|
|
return f.ctx.SQLQuery
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) SetQuery(query interface{}) {
|
|
// In funcspec, we could modify the SQL string, but this should be done cautiously
|
|
if sqlQuery, ok := query.(string); ok {
|
|
f.ctx.SQLQuery = sqlQuery
|
|
}
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) GetResult() interface{} {
|
|
return f.ctx.Result
|
|
}
|
|
|
|
func (f *funcSpecSecurityContext) SetResult(result interface{}) {
|
|
f.ctx.Result = result
|
|
}
|