mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-01 19:20:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec8d4d2c77 | ||
|
|
a3287f3b53 | ||
|
|
1e4a76643d |
@@ -4,10 +4,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/dbtrace"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/dbtrace"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -140,11 +140,16 @@ func applyRowSecurity(secCtx SecurityContext, securityList *SecurityList) error
|
||||
|
||||
// A filter that cannot be attached must fail the request; silently
|
||||
// skipping it would expose every row.
|
||||
selectQuery, ok := secCtx.GetQuery().(common.SelectQuery)
|
||||
if !ok {
|
||||
switch q := secCtx.GetQuery().(type) {
|
||||
case common.SelectQuery:
|
||||
secCtx.SetQuery(q.Where(whereClause, whereArgs...))
|
||||
case common.UpdateQuery:
|
||||
secCtx.SetQuery(q.Where(whereClause, whereArgs...))
|
||||
case common.DeleteQuery:
|
||||
secCtx.SetQuery(q.Where(whereClause, whereArgs...))
|
||||
default:
|
||||
return fmt.Errorf("row security: query type %T on %s.%s does not support Where", secCtx.GetQuery(), schema, tablename)
|
||||
}
|
||||
secCtx.SetQuery(selectQuery.Where(whereClause, whereArgs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -214,6 +214,32 @@ func (q *recordingQuery) Where(query string, args ...interface{}) common.SelectQ
|
||||
return q
|
||||
}
|
||||
|
||||
// recordingUpdateQuery is a common.UpdateQuery that records Where calls.
|
||||
type recordingUpdateQuery struct {
|
||||
common.UpdateQuery
|
||||
clauses []string
|
||||
args [][]any
|
||||
}
|
||||
|
||||
func (q *recordingUpdateQuery) Where(query string, args ...interface{}) common.UpdateQuery {
|
||||
q.clauses = append(q.clauses, query)
|
||||
q.args = append(q.args, args)
|
||||
return q
|
||||
}
|
||||
|
||||
// recordingDeleteQuery is a common.DeleteQuery that records Where calls.
|
||||
type recordingDeleteQuery struct {
|
||||
common.DeleteQuery
|
||||
clauses []string
|
||||
args [][]any
|
||||
}
|
||||
|
||||
func (q *recordingDeleteQuery) Where(query string, args ...interface{}) common.DeleteQuery {
|
||||
q.clauses = append(q.clauses, query)
|
||||
q.args = append(q.args, args)
|
||||
return q
|
||||
}
|
||||
|
||||
// Test applyRowSecurity
|
||||
func TestApplyRowSecurity(t *testing.T) {
|
||||
type TestModel struct {
|
||||
@@ -278,6 +304,33 @@ func TestApplyRowSecurity(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("filter is attached to update and delete queries", func(t *testing.T) {
|
||||
provider := &mockSecurityProvider{rowSecurity: RowSecurity{
|
||||
Schema: "public", Tablename: "orders", Template: "user_id = {UserID}", UserID: 1,
|
||||
}}
|
||||
secList, _ := NewSecurityList(provider)
|
||||
ctx := context.Background()
|
||||
_, _ = secList.LoadRowSecurity(ctx, 1, "public", "orders", false)
|
||||
|
||||
uq := &recordingUpdateQuery{}
|
||||
dq := &recordingDeleteQuery{}
|
||||
for name, q := range map[string]interface{}{"update": uq, "delete": dq} {
|
||||
secCtx := &mockSecurityContext{
|
||||
ctx: ctx, userID: 1, hasUser: true, schema: "public", entity: "orders",
|
||||
model: &TestModel{}, query: q,
|
||||
}
|
||||
if err := ApplyRowSecurity(secCtx, secList); err != nil {
|
||||
t.Fatalf("%s: expected no error, got %v", name, err)
|
||||
}
|
||||
}
|
||||
if len(uq.clauses) != 1 || uq.clauses[0] != "user_id = ?" || uq.args[0][0] != 1 {
|
||||
t.Fatalf("update: filter not attached correctly: %v %v", uq.clauses, uq.args)
|
||||
}
|
||||
if len(dq.clauses) != 1 || dq.clauses[0] != "user_id = ?" || dq.args[0][0] != 1 {
|
||||
t.Fatalf("delete: filter not attached correctly: %v %v", dq.clauses, dq.args)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("user context is bound as its id, never rendered into SQL", func(t *testing.T) {
|
||||
uc := &UserContext{UserID: 7, SessionID: "sess_secret", UserName: "x' OR '1'='1"}
|
||||
provider := &mockSecurityProvider{rowSecurity: RowSecurity{
|
||||
|
||||
@@ -11,9 +11,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/cache"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/dbtrace"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
// DatabaseKeyStoreOptions configures DatabaseKeyStore.
|
||||
|
||||
@@ -11,10 +11,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/cache"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/dbtrace"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
// Production-Ready Authenticators
|
||||
|
||||
Reference in New Issue
Block a user