fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! feat(permissions): Addeding system user support for permission check v2

This commit is contained in:
Iraq Jaber
2025-03-12 11:39:25 +00:00
parent 306ce97828
commit 255d77a6e9
12 changed files with 87 additions and 46 deletions

View File

@@ -1172,6 +1172,34 @@ DefaultInstance:
# If an audit log retention is set using an instance limit, it will overwrite the system default.
AuditLogRetention: 0s # ZITADEL_AUDITLOGRETENTION
SystemAuthZ:
# Configure the RolePermissionMappings by environment variable using JSON notation:
# ZITADEL_INTERNALAUTHZ_ROLEPERMISSIONMAPPINGS='[{"role": "IAM_OWNER", "permissions": ["iam.write"]}, {"role": "ORG_OWNER", "permissions": ["org.write"]}]'
# Beware that if you configure the RolePermissionMappings by environment variable, all the default RolePermissionMappings are lost.
#
# Warning: RolePermissionMappings are synhronized to the database.
# Changes here will only be applied after running `zitadel setup` or `zitadel start-from-setup`.
RolePermissionMappings:
- Role: "SYSTEM_OWNER"
Permissions:
- "system.instance.read"
- "system.instance.write"
- "system.instance.delete"
- "system.domain.read"
- "system.domain.write"
- "system.domain.delete"
- "system.debug.read"
- "system.debug.write"
- "system.debug.delete"
- "system.feature.read"
- "system.feature.write"
- "system.feature.delete"
- "system.limits.write"
- "system.limits.delete"
- "system.quota.write"
- "system.quota.delete"
- "system.iam.member.read"
InternalAuthZ:
# Configure the RolePermissionMappings by environment variable using JSON notation:
# ZITADEL_INTERNALAUTHZ_ROLEPERMISSIONMAPPINGS='[{"role": "IAM_OWNER", "permissions": ["iam.write"]}, {"role": "ORG_OWNER", "permissions": ["org.write"]}]'

View File

@@ -147,7 +147,7 @@ func projections(
sessionTokenVerifier,
func(q *query.Queries) domain.PermissionCheck {
return func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, nil, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
}
},
0,
@@ -184,7 +184,7 @@ func projections(
keys.Target,
&http.Client{},
func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, authZRepo, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, authZRepo, nil, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
},
sessionTokenVerifier,
config.OIDC.DefaultAccessTokenLifetime,

View File

@@ -410,7 +410,7 @@ func startCommandsQueries(
sessionTokenVerifier,
func(q *query.Queries) domain.PermissionCheck {
return func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, nil, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
}
},
0, // not needed for projections
@@ -435,7 +435,7 @@ func startCommandsQueries(
authZRepo, err := authz.Start(queries, eventstoreClient, dbClient, keys.OIDC, config.ExternalSecure)
logging.OnError(err).Fatal("unable to start authz repo")
permissionCheck := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, authZRepo, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, authZRepo, nil, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
}
commands, err := command.StartCommands(ctx,

View File

@@ -1,6 +1,7 @@
package start
import (
"fmt"
"time"
"github.com/mitchellh/mapstructure"
@@ -11,7 +12,7 @@ import (
"github.com/zitadel/zitadel/cmd/hooks"
"github.com/zitadel/zitadel/internal/actions"
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/http/middleware"
"github.com/zitadel/zitadel/internal/api/oidc"
"github.com/zitadel/zitadel/internal/api/saml"
@@ -65,12 +66,13 @@ type Config struct {
Login login.Config
Console console.Config
AssetStorage static_config.AssetStorageConfig
InternalAuthZ internal_authz.Config
InternalAuthZ authz.Config
SystemAuthZ authz.Config
SystemDefaults systemdefaults.SystemDefaults
EncryptionKeys *encryption.EncryptionKeyConfig
DefaultInstance command.InstanceSetup
AuditLogRetention time.Duration
SystemAPIUsers map[string]*internal_authz.SystemAPIUser
SystemAPIUsers map[string]*authz.SystemAPIUser
CustomerPortal string
Machine *id.Config
Actions *actions.Config
@@ -94,12 +96,12 @@ func MustNewConfig(v *viper.Viper) *Config {
err := v.Unmarshal(config,
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
hooks.SliceTypeStringDecode[*domain.CustomMessageText],
hooks.SliceTypeStringDecode[internal_authz.RoleMapping],
hooks.MapTypeStringDecode[string, *internal_authz.SystemAPIUser],
hooks.SliceTypeStringDecode[authz.RoleMapping],
hooks.MapTypeStringDecode[string, *authz.SystemAPIUser],
hooks.MapHTTPHeaderStringDecode,
database.DecodeHook,
actions.HTTPConfigDecodeHook,
hook.EnumHookFunc(internal_authz.MemberTypeString),
hook.EnumHookFunc(authz.MemberTypeString),
hooks.MapTypeStringDecode[domain.Feature, any],
hooks.SliceTypeStringDecode[*command.SetQuota],
hook.Base64ToBytesHookFunc(),
@@ -129,6 +131,7 @@ func MustNewConfig(v *viper.Viper) *Config {
// Copy the global role permissions mappings to the instance until we allow instance-level configuration over the API.
config.DefaultInstance.RolePermissionMappings = config.InternalAuthZ.RolePermissionMappings
fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> config.SystemAuthZ = %+v\n", config.SystemAuthZ)
return config
}

View File

@@ -174,6 +174,12 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server
return fmt.Errorf("unable to start caches: %w", err)
}
systemPermissions := make([]string, 0)
// for _, roleMapping := range config.SystemAuthZ.RolePermissionMappings {
// systemPermissions = append(systemPermissions, roleMapping.Permissions...)
// }
// config.InternalAuthZ.SystemUserPermissions = systemPermissions
queries, err := query.StartQueries(
ctx,
eventstoreClient,
@@ -192,7 +198,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server
sessionTokenVerifier,
func(q *query.Queries) domain.PermissionCheck {
return func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, &authz_es.UserMembershipRepo{Queries: q}, systemPermissions, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
}
},
config.AuditLogRetention,
@@ -208,7 +214,7 @@ func startZitadel(ctx context.Context, config *Config, masterKey string, server
return fmt.Errorf("error starting authz repo: %w", err)
}
permissionCheck := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
return internal_authz.CheckPermission(ctx, authZRepo, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
return internal_authz.CheckPermission(ctx, authZRepo, systemPermissions, config.InternalAuthZ.RolePermissionMappings, permission, orgID, resourceID)
}
storage, err := config.AssetStorage.NewStorage(dbClient.DB)
@@ -407,7 +413,8 @@ func startAPIs(
http_util.WithMaxAge(int(math.Floor(config.Quotas.Access.ExhaustedCookieMaxAge.Seconds()))),
)
limitingAccessInterceptor := middleware.NewAccessInterceptor(accessSvc, exhaustedCookieHandler, &config.Quotas.Access.AccessConfig)
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.InternalAuthZ, tlsConfig, config.ExternalDomain, append(config.InstanceHostHeaders, config.PublicHostHeaders...), limitingAccessInterceptor)
apis, err := api.New(ctx, config.Port, router, queries, verifier, config.SystemAuthZ, config.InternalAuthZ, tlsConfig, config.ExternalDomain, append(config.InstanceHostHeaders, config.PublicHostHeaders...), limitingAccessInterceptor)
if err != nil {
return nil, fmt.Errorf("error creating api %w", err)
}
@@ -600,7 +607,7 @@ func listen(ctx context.Context, router *mux.Router, port uint16, tlsConfig *tls
go func() {
logging.Infof("server is listening on %s", lis.Addr().String())
if tlsConfig != nil {
//we don't need to pass the files here, because we already initialized the TLS config on the server
// we don't need to pass the files here, because we already initialized the TLS config on the server
errCh <- http1Server.ServeTLS(lis, "", "")
} else {
errCh <- http1Server.Serve(lis)