perf(query): org permission function for resources (#9677)

# Which Problems Are Solved

Classic permission checks execute for every returned row on resource
based search APIs. Complete background and problem definition can be
found here: https://github.com/zitadel/zitadel/issues/9188

# How the Problems Are Solved

- PermissionClause function now support dynamic query building, so it
supports multiple cases.
- PermissionClause is applied to all list resources which support org
level permissions.
- Wrap permission logic into wrapper functions so we keep the business
logic clean.

# Additional Changes

- Handle org ID optimization in the query package, so it is reusable for
all resources, instead of extracting the filter in the API.
- Cleanup and test system user conversion in the authz package. (context
middleware)
- Fix: `core_integration_db_up` make recipe was missing the postgres
service.

# Additional Context

- Related to https://github.com/zitadel/zitadel/issues/9190
This commit is contained in:
Tim Möhlmann
2025-04-15 19:38:25 +03:00
committed by GitHub
parent 3b8a2ab811
commit a2f60f2e7a
23 changed files with 741 additions and 172 deletions

View File

@@ -225,3 +225,37 @@ func (d *NullDuration) Scan(src any) error {
d.Duration, d.Valid = time.Duration(*duration), true
return nil
}
// JSONArray allows sending and receiving JSON arrays to and from the database.
// It implements the [database/sql.Scanner] and [database/sql/driver.Valuer] interfaces.
// Values are marshaled and unmarshaled using the [encoding/json] package.
type JSONArray[T any] []T
// NewJSONArray wraps an existing slice into a JSONArray.
func NewJSONArray[T any](a []T) JSONArray[T] {
return JSONArray[T](a)
}
// Scan implements the [database/sql.Scanner] interface.
func (a *JSONArray[T]) Scan(src any) error {
if src == nil {
*a = nil
return nil
}
bytes := src.([]byte)
if len(bytes) == 0 {
*a = nil
return nil
}
return json.Unmarshal(bytes, a)
}
// Value implements the [database/sql/driver.Valuer] interface.
func (a JSONArray[T]) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
return json.Marshal(a)
}

View File

@@ -5,6 +5,7 @@ import (
"database/sql/driver"
"testing"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -452,3 +453,89 @@ func TestDuration_Scan(t *testing.T) {
})
}
}
func TestJSONArray_Scan(t *testing.T) {
type args struct {
src any
}
tests := []struct {
name string
args args
want *JSONArray[string]
wantErr bool
}{
{
name: "nil",
args: args{src: nil},
want: new(JSONArray[string]),
wantErr: false,
},
{
name: "zero bytes",
args: args{src: []byte("")},
want: new(JSONArray[string]),
wantErr: false,
},
{
name: "empty",
args: args{src: []byte("[]")},
want: gu.Ptr(JSONArray[string]{}),
wantErr: false,
},
{
name: "ok",
args: args{src: []byte("[\"a\", \"b\"]")},
want: gu.Ptr(JSONArray[string]{"a", "b"}),
wantErr: false,
},
{
name: "json error",
args: args{src: []byte("{\"a\": \"b\"}")},
want: new(JSONArray[string]),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := new(JSONArray[string])
err := got.Scan(tt.args.src)
if tt.wantErr {
assert.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestJSONArray_Value(t *testing.T) {
tests := []struct {
name string
a []string
want driver.Value
}{
{
name: "nil",
a: nil,
want: nil,
},
{
name: "empty",
a: []string{},
want: []byte("[]"),
},
{
name: "ok",
a: []string{"a", "b"},
want: []byte("[\"a\",\"b\"]"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewJSONArray(tt.a).Value()
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}