2025-04-15 19:38:25 +03:00
|
|
|
package query
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
|
|
|
|
2025-04-22 11:42:59 +03:00
|
|
|
"github.com/muhlemmer/gu"
|
2025-04-15 19:38:25 +03:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
|
|
"github.com/zitadel/zitadel/internal/database"
|
|
|
|
domain_pkg "github.com/zitadel/zitadel/internal/domain"
|
|
|
|
"github.com/zitadel/zitadel/internal/feature"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestPermissionClause(t *testing.T) {
|
|
|
|
var permissions = []authz.SystemUserPermissions{
|
|
|
|
{
|
|
|
|
MemberType: authz.MemberTypeOrganization,
|
|
|
|
AggregateID: "orgID",
|
|
|
|
Permissions: []string{"permission1", "permission2"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
MemberType: authz.MemberTypeIAM,
|
|
|
|
Permissions: []string{"permission2", "permission3"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
ctx := authz.WithInstanceID(context.Background(), "instanceID")
|
|
|
|
ctx = authz.SetCtxData(ctx, authz.CtxData{
|
|
|
|
UserID: "userID",
|
|
|
|
SystemUserPermissions: permissions,
|
|
|
|
})
|
|
|
|
|
|
|
|
type args struct {
|
|
|
|
ctx context.Context
|
|
|
|
orgIDCol Column
|
|
|
|
permission string
|
|
|
|
options []PermissionOption
|
|
|
|
}
|
|
|
|
tests := []struct {
|
2025-04-22 11:42:59 +03:00
|
|
|
name string
|
|
|
|
args args
|
|
|
|
wantSql string
|
|
|
|
wantArgs []any
|
2025-04-15 19:38:25 +03:00
|
|
|
}{
|
|
|
|
{
|
2025-04-22 11:42:59 +03:00
|
|
|
name: "org, no options",
|
2025-04-15 19:38:25 +03:00
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: UserResourceOwnerCol,
|
|
|
|
permission: "permission1",
|
|
|
|
},
|
2025-04-22 11:42:59 +03:00
|
|
|
wantSql: "INNER JOIN eventstore.permitted_orgs(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.users14.resource_owner = ANY(permissions.org_ids))",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
(*string)(nil),
|
2025-04-15 19:38:25 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2025-04-22 11:42:59 +03:00
|
|
|
name: "org, owned rows option",
|
2025-04-15 19:38:25 +03:00
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: UserResourceOwnerCol,
|
|
|
|
permission: "permission1",
|
|
|
|
options: []PermissionOption{
|
|
|
|
OwnedRowsPermissionOption(UserIDCol),
|
|
|
|
},
|
|
|
|
},
|
2025-04-22 11:42:59 +03:00
|
|
|
wantSql: "INNER JOIN eventstore.permitted_orgs(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.users14.resource_owner = ANY(permissions.org_ids) OR projections.users14.id = ?)",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
(*string)(nil),
|
|
|
|
"userID",
|
2025-04-15 19:38:25 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2025-04-22 11:42:59 +03:00
|
|
|
name: "org, connection rows option",
|
2025-04-15 19:38:25 +03:00
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: UserResourceOwnerCol,
|
|
|
|
permission: "permission1",
|
|
|
|
options: []PermissionOption{
|
|
|
|
OwnedRowsPermissionOption(UserIDCol),
|
|
|
|
ConnectionPermissionOption(UserStateCol, "bar"),
|
|
|
|
},
|
|
|
|
},
|
2025-04-22 11:42:59 +03:00
|
|
|
wantSql: "INNER JOIN eventstore.permitted_orgs(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.users14.resource_owner = ANY(permissions.org_ids) OR projections.users14.id = ? OR projections.users14.state = ?)",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
(*string)(nil),
|
|
|
|
"userID",
|
|
|
|
"bar",
|
2025-04-15 19:38:25 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2025-04-22 11:42:59 +03:00
|
|
|
name: "org, with ID",
|
2025-04-15 19:38:25 +03:00
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: UserResourceOwnerCol,
|
|
|
|
permission: "permission1",
|
|
|
|
options: []PermissionOption{
|
|
|
|
SingleOrgPermissionOption([]SearchQuery{
|
|
|
|
mustSearchQuery(NewUserDisplayNameSearchQuery("zitadel", TextContains)),
|
|
|
|
mustSearchQuery(NewUserResourceOwnerSearchQuery("orgID", TextEquals)),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
2025-04-22 11:42:59 +03:00
|
|
|
wantSql: "INNER JOIN eventstore.permitted_orgs(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.users14.resource_owner = ANY(permissions.org_ids))",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
gu.Ptr("orgID"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "project",
|
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: ProjectColumnResourceOwner,
|
|
|
|
permission: "permission1",
|
|
|
|
options: []PermissionOption{
|
|
|
|
WithProjectsPermissionOption(ProjectColumnID),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantSql: "INNER JOIN eventstore.permitted_projects(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.projects4.resource_owner = ANY(permissions.org_ids) OR projections.projects4.id = ANY(permissions.project_ids))",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
(*string)(nil),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "project, single org",
|
|
|
|
args: args{
|
|
|
|
ctx: ctx,
|
|
|
|
orgIDCol: ProjectColumnResourceOwner,
|
|
|
|
permission: "permission1",
|
|
|
|
options: []PermissionOption{
|
|
|
|
WithProjectsPermissionOption(ProjectColumnID),
|
|
|
|
SingleOrgPermissionOption([]SearchQuery{
|
|
|
|
mustSearchQuery(NewProjectResourceOwnerSearchQuery("orgID")),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantSql: "INNER JOIN eventstore.permitted_projects(?, ?, ?, ?, ?) permissions ON (permissions.instance_permitted OR projections.projects4.resource_owner = ANY(permissions.org_ids) OR projections.projects4.id = ANY(permissions.project_ids))",
|
|
|
|
wantArgs: []any{
|
|
|
|
"instanceID",
|
|
|
|
"userID",
|
|
|
|
database.NewJSONArray(permissions),
|
|
|
|
"permission1",
|
|
|
|
gu.Ptr("orgID"),
|
2025-04-15 19:38:25 +03:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2025-04-22 11:42:59 +03:00
|
|
|
gotSql, gotArgs := PermissionClause(tt.args.ctx, tt.args.orgIDCol, tt.args.permission, tt.args.options...)
|
|
|
|
assert.Equal(t, tt.wantSql, gotSql)
|
|
|
|
assert.Equal(t, tt.wantArgs, gotArgs)
|
2025-04-15 19:38:25 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func mustSearchQuery(q SearchQuery, err error) SearchQuery {
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return q
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPermissionV2(t *testing.T) {
|
|
|
|
type args struct {
|
|
|
|
ctx context.Context
|
|
|
|
cf domain_pkg.PermissionCheck
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
|
|
|
want bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "feature disabled, no permission check",
|
|
|
|
args: args{
|
|
|
|
ctx: context.Background(),
|
|
|
|
cf: nil,
|
|
|
|
},
|
|
|
|
want: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "feature enabled, no permission check",
|
|
|
|
args: args{
|
|
|
|
ctx: authz.WithFeatures(context.Background(), feature.Features{
|
|
|
|
PermissionCheckV2: true,
|
|
|
|
}),
|
|
|
|
cf: nil,
|
|
|
|
},
|
|
|
|
want: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "feature enabled, with permission check",
|
|
|
|
args: args{
|
|
|
|
ctx: authz.WithFeatures(context.Background(), feature.Features{
|
|
|
|
PermissionCheckV2: true,
|
|
|
|
}),
|
|
|
|
cf: func(context.Context, string, string, string) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "feature disabled, with permission check",
|
|
|
|
args: args{
|
|
|
|
ctx: authz.WithFeatures(context.Background(), feature.Features{
|
|
|
|
PermissionCheckV2: false,
|
|
|
|
}),
|
|
|
|
cf: func(context.Context, string, string, string) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
got := PermissionV2(tt.args.ctx, tt.args.cf)
|
|
|
|
assert.Equal(t, tt.want, got)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|