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

@@ -106,12 +106,26 @@ func idpLinksCheckPermission(ctx context.Context, links *IDPUserLinks, permissio
)
}
func idpLinksPermissionCheckV2(ctx context.Context, query sq.SelectBuilder, enabled bool, queries *IDPUserLinksSearchQuery) sq.SelectBuilder {
if !enabled {
return query
}
return query.Where(PermissionClause(
ctx,
IDPUserLinkResourceOwnerCol,
domain.PermissionUserRead,
SingleOrgPermissionOption(queries.Queries),
OwnedRowsPermissionOption(IDPUserLinkUserIDCol),
))
}
func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, permissionCheck domain.PermissionCheck) (idps *IDPUserLinks, err error) {
links, err := q.idpUserLinks(ctx, queries, false)
permissionCheckV2 := PermissionV2(ctx, permissionCheck)
links, err := q.idpUserLinks(ctx, queries, permissionCheckV2)
if err != nil {
return nil, err
}
if permissionCheck != nil && len(links.Links) > 0 {
if permissionCheck != nil && len(links.Links) > 0 && !permissionCheckV2 {
// when userID for query is provided, only one check has to be done
if queries.hasUserID() {
if err := userCheckPermission(ctx, links.Links[0].ResourceOwner, links.Links[0].UserID, permissionCheck); err != nil {
@@ -124,14 +138,15 @@ func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQ
return links, nil
}
func (q *Queries) idpUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, withOwnerRemoved bool) (idps *IDPUserLinks, err error) {
func (q *Queries) idpUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, permissionCheckV2 bool) (idps *IDPUserLinks, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
query, scan := prepareIDPUserLinksQuery()
eq := sq.Eq{IDPUserLinkInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID()}
if !withOwnerRemoved {
eq[IDPUserLinkOwnerRemovedCol.identifier()] = false
query = idpLinksPermissionCheckV2(ctx, query, permissionCheckV2, queries)
eq := sq.Eq{
IDPUserLinkInstanceIDCol.identifier(): authz.GetInstance(ctx).InstanceID(),
IDPUserLinkOwnerRemovedCol.identifier(): false,
}
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
if err != nil {