From fcb05fd561443b0a87dcb3587a6904116f6db96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Mon, 13 Oct 2025 18:04:39 +0300 Subject: [PATCH] feat(relational): command permission check (#10771) # Which Problems Are Solved There is no implementation of the permission framework in the new command pattern. We risk forgetting permission options as we are already migrating the service layer to the new pattern. # How the Problems Are Solved Use an permission checker interface, which will be a sub-interface of the Permission Repository. The permission checker is passed in the command executor and invoker as part of the command options. From here the implementation can call the correct permission method. This PR proposes a check method for each [level](https://github.com/zitadel/zitadel/wiki/Internal-AuthZ#levels). # Additional Changes - none # Additional Context - Draft design: https://github.com/zitadel/zitadel/wiki/Internal-AuthZ#enforcement - Related for queries: https://github.com/zitadel/zitadel/pull/10756 --- backend/v3/domain/command.go | 10 ++++--- backend/v3/domain/permission.go | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 backend/v3/domain/permission.go diff --git a/backend/v3/domain/command.go b/backend/v3/domain/command.go index cd0da4d0d..8dd301f53 100644 --- a/backend/v3/domain/command.go +++ b/backend/v3/domain/command.go @@ -28,8 +28,9 @@ type Invoker interface { // CommandOpts are passed to each command // it provides common fields used by commands like the database client. type CommandOpts struct { - DB database.QueryExecutor - Invoker Invoker + DB database.QueryExecutor + Invoker Invoker + Permissions PermissionChecker } type ensureTxOpts struct { @@ -101,8 +102,9 @@ func DefaultOpts(invoker Invoker) *CommandOpts { invoker = &noopInvoker{} } return &CommandOpts{ - DB: pool, - Invoker: invoker, + DB: pool, + Invoker: invoker, + Permissions: &noopPermissionChecker{}, // prevent panics for now } } diff --git a/backend/v3/domain/permission.go b/backend/v3/domain/permission.go new file mode 100644 index 000000000..3484085fe --- /dev/null +++ b/backend/v3/domain/permission.go @@ -0,0 +1,48 @@ +package domain + +import "context" + +// PermissionRepository is the interface that manages and checks Zitadel permissions. +// +// TODO(muhlemmer): This just defines the checker methods, rest to be done later. +type PermissionRepository interface { + PermissionChecker +} + +// PermissionChecker defines the methods needed to check permissions. +type PermissionChecker interface { + // Check if the authenticated user has the given permission on instance level. + CheckInstancePermission(ctx context.Context, permission string) error + + // Check if the authenticated user has the given permission on the given organization. + // A permission may be inherited from the instance level. + CheckOrganizationPermission(ctx context.Context, permission, orgID string) error + + // Check if the authenticated user has the given permission on the given project. + // A permission may be inherited from the instance or organization level. + CheckProjectPermission(ctx context.Context, permission, projectID string) error + + // Check if the authenticated user has the given permission on the given project grant. + // A permission may be inherited from the instance or granted organization level. + CheckProjectGrantPermission(ctx context.Context, permission, projectGrantID string) error +} + +type noopPermissionChecker struct{} + +var _ PermissionChecker = (*noopPermissionChecker)(nil) + +func (*noopPermissionChecker) CheckInstancePermission(context.Context, string) error { + return nil +} + +func (*noopPermissionChecker) CheckOrganizationPermission(context.Context, string, string) error { + return nil +} + +func (*noopPermissionChecker) CheckProjectPermission(context.Context, string, string) error { + return nil +} + +func (*noopPermissionChecker) CheckProjectGrantPermission(context.Context, string, string) error { + return nil +}