mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
feat: support whole config as env (#6336)
* fix existing env vars * feat: support all config by env * cleanup * remove system users hook * decode system users in setup
This commit is contained in:
parent
32c7efea73
commit
19af2f7372
@ -1,7 +1,7 @@
|
|||||||
Log:
|
Log:
|
||||||
Level: info # ZITADEL_LOG_LEVEL
|
Level: info # ZITADEL_LOG_LEVEL
|
||||||
Formatter:
|
Formatter:
|
||||||
Format: text # ZITADEL_LOG_LEVEL
|
Format: text # ZITADEL_LOG_FORMATTER_FORMAT
|
||||||
|
|
||||||
# Exposes metrics on /debug/metrics
|
# Exposes metrics on /debug/metrics
|
||||||
Metrics:
|
Metrics:
|
||||||
@ -29,7 +29,7 @@ Telemetry:
|
|||||||
# As long as Enabled is true, ZITADEL tries to send usage data to the configured Telemetry.Endpoints.
|
# As long as Enabled is true, ZITADEL tries to send usage data to the configured Telemetry.Endpoints.
|
||||||
# Data is projected by ZITADEL even if Enabled is false.
|
# Data is projected by ZITADEL even if Enabled is false.
|
||||||
# This means that switching this to true makes ZITADEL try to send past data.
|
# This means that switching this to true makes ZITADEL try to send past data.
|
||||||
Enabled: false
|
Enabled: false # ZITADEL_TELEMETRY_ENABLED
|
||||||
# Push telemetry data to all these endpoints at least once using an HTTP POST request.
|
# Push telemetry data to all these endpoints at least once using an HTTP POST request.
|
||||||
# If one endpoint returns an unsuccessful response code or times out,
|
# If one endpoint returns an unsuccessful response code or times out,
|
||||||
# ZITADEL retries to push the data point to all configured endpoints until it succeeds.
|
# ZITADEL retries to push the data point to all configured endpoints until it succeeds.
|
||||||
@ -40,7 +40,9 @@ Telemetry:
|
|||||||
Endpoints:
|
Endpoints:
|
||||||
- https://httpbin.org/post
|
- https://httpbin.org/post
|
||||||
# These headers are sent with every request to the configured endpoints.
|
# These headers are sent with every request to the configured endpoints.
|
||||||
Headers:
|
# Configure headers by environment variable using a JSON string with header values as arrays, like this:
|
||||||
|
# ZITADEL_TELEMETRY_HEADERS='{"header1": ["value1"], "header2": ["value2", "value3"]}'
|
||||||
|
Headers: # ZITADEL_TELEMETRY_HEADERS
|
||||||
# single-value: "single-value"
|
# single-value: "single-value"
|
||||||
# multi-value:
|
# multi-value:
|
||||||
# - "multi-value-1"
|
# - "multi-value-1"
|
||||||
@ -85,7 +87,7 @@ HTTP2HostHeader: ":authority" # ZITADEL_HTTP2HOSTHEADER
|
|||||||
# Header name of HTTP1 calls from which the instance will be matched
|
# Header name of HTTP1 calls from which the instance will be matched
|
||||||
HTTP1HostHeader: "host" # ZITADEL_HTTP1HOSTHEADER
|
HTTP1HostHeader: "host" # ZITADEL_HTTP1HOSTHEADER
|
||||||
|
|
||||||
WebAuthNName: ZITADEL # ZITADEL_WEBAUTHN_NAME
|
WebAuthNName: ZITADEL # ZITADEL_WEBAUTHNNAME
|
||||||
|
|
||||||
Database:
|
Database:
|
||||||
# ZITADEL manages three database connection pools.
|
# ZITADEL manages three database connection pools.
|
||||||
@ -170,7 +172,7 @@ Machine:
|
|||||||
Enabled: true # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_ENABLED
|
Enabled: true # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_ENABLED
|
||||||
Url: "http://metadata.google.internal/computeMetadata/v1/instance/id" # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_URL
|
Url: "http://metadata.google.internal/computeMetadata/v1/instance/id" # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_URL
|
||||||
Headers:
|
Headers:
|
||||||
"Metadata-Flavor": "Google" # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_HEADERS
|
"Metadata-Flavor": "Google"
|
||||||
#
|
#
|
||||||
# AWS EC2 IMDSv1 Configuration: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
|
# AWS EC2 IMDSv1 Configuration: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
|
||||||
# Webhook:
|
# Webhook:
|
||||||
@ -205,7 +207,7 @@ Projections:
|
|||||||
# Time interval between scheduled projections
|
# Time interval between scheduled projections
|
||||||
RequeueEvery: 60s # ZITADEL_PROJECTIONS_REQUEUEEVERY
|
RequeueEvery: 60s # ZITADEL_PROJECTIONS_REQUEUEEVERY
|
||||||
# Time between retried database statements resulting from projected events
|
# Time between retried database statements resulting from projected events
|
||||||
RetryFailedAfter: 1s # ZITADEL_PROJECTIONS_RETRYFAILED
|
RetryFailedAfter: 1s # ZITADEL_PROJECTIONS_RETRYFAILEDAFTER
|
||||||
# Retried execution number of database statements resulting from projected events
|
# Retried execution number of database statements resulting from projected events
|
||||||
MaxFailureCount: 5 # ZITADEL_PROJECTIONS_MAXFAILURECOUNT
|
MaxFailureCount: 5 # ZITADEL_PROJECTIONS_MAXFAILURECOUNT
|
||||||
# Limit of returned events per query
|
# Limit of returned events per query
|
||||||
@ -378,28 +380,28 @@ Console:
|
|||||||
EncryptionKeys:
|
EncryptionKeys:
|
||||||
DomainVerification:
|
DomainVerification:
|
||||||
EncryptionKeyID: "domainVerificationKey" # ZITADEL_ENCRYPTIONKEYS_DOMAINVERIFICATION_ENCRYPTIONKEYID
|
EncryptionKeyID: "domainVerificationKey" # ZITADEL_ENCRYPTIONKEYS_DOMAINVERIFICATION_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_DOMAINVERIFICATION_DECRYPTIONKEYIDS (comma separated list)
|
||||||
IDPConfig:
|
IDPConfig:
|
||||||
EncryptionKeyID: "idpConfigKey" # ZITADEL_ENCRYPTIONKEYS_IDPCONFIG_ENCRYPTIONKEYID
|
EncryptionKeyID: "idpConfigKey" # ZITADEL_ENCRYPTIONKEYS_IDPCONFIG_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_IDPCONFIG_DECRYPTIONKEYIDS (comma separated list)
|
||||||
OIDC:
|
OIDC:
|
||||||
EncryptionKeyID: "oidcKey" # ZITADEL_ENCRYPTIONKEYS_OIDC_ENCRYPTIONKEYID
|
EncryptionKeyID: "oidcKey" # ZITADEL_ENCRYPTIONKEYS_OIDC_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_OIDC_DECRYPTIONKEYIDS (comma separated list)
|
||||||
SAML:
|
SAML:
|
||||||
EncryptionKeyID: "samlKey" # ZITADEL_ENCRYPTIONKEYS_SAML_ENCRYPTIONKEYID
|
EncryptionKeyID: "samlKey" # ZITADEL_ENCRYPTIONKEYS_SAML_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SAML_DECRYPTIONKEYIDS (comma separated list)
|
||||||
OTP:
|
OTP:
|
||||||
EncryptionKeyID: "otpKey" # ZITADEL_ENCRYPTIONKEYS_OTP_ENCRYPTIONKEYID
|
EncryptionKeyID: "otpKey" # ZITADEL_ENCRYPTIONKEYS_OTP_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_OTP_DECRYPTIONKEYIDS (comma separated list)
|
||||||
SMS:
|
SMS:
|
||||||
EncryptionKeyID: "smsKey" # ZITADEL_ENCRYPTIONKEYS_SMS_ENCRYPTIONKEYID
|
EncryptionKeyID: "smsKey" # ZITADEL_ENCRYPTIONKEYS_SMS_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SMS_DECRYPTIONKEYIDS (comma separated list)
|
||||||
SMTP:
|
SMTP:
|
||||||
EncryptionKeyID: "smtpKey" # ZITADEL_ENCRYPTIONKEYS_SMTP_ENCRYPTIONKEYID
|
EncryptionKeyID: "smtpKey" # ZITADEL_ENCRYPTIONKEYS_SMTP_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SMTP_DECRYPTIONKEYIDS (comma separated list)
|
||||||
User:
|
User:
|
||||||
EncryptionKeyID: "userKey" # ZITADEL_ENCRYPTIONKEYS_USER_ENCRYPTIONKEYID
|
EncryptionKeyID: "userKey" # ZITADEL_ENCRYPTIONKEYS_USER_ENCRYPTIONKEYID
|
||||||
DecryptionKeyIDs:
|
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_USER_DECRYPTIONKEYIDS (comma separated list)
|
||||||
CSRFCookieKeyID: "csrfCookieKey" # ZITADEL_ENCRYPTIONKEYS_CSRFCOOKIEKEYID
|
CSRFCookieKeyID: "csrfCookieKey" # ZITADEL_ENCRYPTIONKEYS_CSRFCOOKIEKEYID
|
||||||
UserAgentCookieKeyID: "userAgentCookieKey" # ZITADEL_ENCRYPTIONKEYS_USERAGENTCOOKIEKEYID
|
UserAgentCookieKeyID: "userAgentCookieKey" # ZITADEL_ENCRYPTIONKEYS_USERAGENTCOOKIEKEYID
|
||||||
|
|
||||||
@ -426,6 +428,8 @@ SystemAPIUsers:
|
|||||||
# - superuser2:
|
# - superuser2:
|
||||||
# # If no memberships are specified, the user has a membership of type System with the role "SYSTEM_OWNER"
|
# # If no memberships are specified, the user has a membership of type System with the role "SYSTEM_OWNER"
|
||||||
# KeyData: <base64 encoded key> # or you can directly embed it as base64 encoded value
|
# KeyData: <base64 encoded key> # or you can directly embed it as base64 encoded value
|
||||||
|
# Configure the SystemAPIUsers by environment variable using JSON notation:
|
||||||
|
# ZITADEL_SYSTEMAPIUSERS='{"systemuser":{"Path":"/path/to/superuser/key.pem"},"systemuser2":{"KeyData":"<base64 encoded key>"}}'
|
||||||
|
|
||||||
#TODO: remove as soon as possible
|
#TODO: remove as soon as possible
|
||||||
SystemDefaults:
|
SystemDefaults:
|
||||||
@ -457,13 +461,13 @@ SystemDefaults:
|
|||||||
# Threads: 4 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_THREADS
|
# Threads: 4 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_THREADS
|
||||||
|
|
||||||
# Hasher:
|
# Hasher:
|
||||||
# Algorithm: "scrypt"
|
# Algorithm: "scrypt" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
||||||
# Cost: 15
|
# Cost: 15 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_COST
|
||||||
|
|
||||||
# Hasher:
|
# Hasher:
|
||||||
# Algorithm: "pbkdf2"
|
# Algorithm: "pbkdf2" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
||||||
# Rounds: 290000
|
# Rounds: 290000 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ROUNDS
|
||||||
# Hash: "sha256" # Can be "sha1", "sha224", "sha256", "sha384" or "sha512"
|
# Hash: "sha256" # Can be "sha1", "sha224", "sha256", "sha384" or "sha512" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_HASH
|
||||||
|
|
||||||
# Verifiers enable the possibility of verifying
|
# Verifiers enable the possibility of verifying
|
||||||
# passwords that are previously hashed using another
|
# passwords that are previously hashed using another
|
||||||
@ -509,7 +513,7 @@ SystemDefaults:
|
|||||||
Actions:
|
Actions:
|
||||||
HTTP:
|
HTTP:
|
||||||
# Wildcard sub domains are currently unsupported
|
# Wildcard sub domains are currently unsupported
|
||||||
DenyList:
|
DenyList: # ZITADEL_ACTIONS_HTTP_DENYLIST (comma separated list)
|
||||||
- localhost
|
- localhost
|
||||||
- "127.0.0.1"
|
- "127.0.0.1"
|
||||||
|
|
||||||
@ -727,6 +731,9 @@ DefaultInstance:
|
|||||||
From: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROM
|
From: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROM
|
||||||
FromName: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROMNAME
|
FromName: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROMNAME
|
||||||
ReplyToAddress: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_REPLYTOADDRESS
|
ReplyToAddress: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_REPLYTOADDRESS
|
||||||
|
# Configure the MessageTexts by environment variable using JSON notation:
|
||||||
|
# ZITADEL_DEFAULTINSTANCE_MESSAGETEXTS='[{"messageTextType": "InitCode", "title": "My custom title"},{"messageTextType": "PasswordReset", "greeting": "Hi there!"}]'
|
||||||
|
# Beware that if you configure the MessageTexts by environment variable, all the default MessageTexts are lost.
|
||||||
MessageTexts:
|
MessageTexts:
|
||||||
- MessageTextType: InitCode
|
- MessageTextType: InitCode
|
||||||
Language: de
|
Language: de
|
||||||
@ -857,7 +864,9 @@ DefaultInstance:
|
|||||||
|
|
||||||
# "actions.all.runs.seconds"
|
# "actions.all.runs.seconds"
|
||||||
# The sum of all actions run durations in seconds
|
# The sum of all actions run durations in seconds
|
||||||
Items:
|
# Configure the Items by environment variable using JSON notation:
|
||||||
|
# ZITADEL_DEFAULTINSTANCE_QUOTAS_ITEMS='[{"unit": "requests.all.authenticated", "notifications": [{"percent": 100}]}]'
|
||||||
|
Items: # ZITADEL_DEFAULTINSTANCE_QUOTAS_ITEMS
|
||||||
# - Unit: "requests.all.authenticated"
|
# - Unit: "requests.all.authenticated"
|
||||||
# # From defines the starting time from which the current quota period is calculated.
|
# # From defines the starting time from which the current quota period is calculated.
|
||||||
# # This is relevant for querying the current usage.
|
# # This is relevant for querying the current usage.
|
||||||
@ -884,6 +893,9 @@ DefaultInstance:
|
|||||||
AuditLogRetention: 0s # ZITADEL_AUDITLOGRETENTION
|
AuditLogRetention: 0s # ZITADEL_AUDITLOGRETENTION
|
||||||
|
|
||||||
InternalAuthZ:
|
InternalAuthZ:
|
||||||
|
# Configure the RolePermissionMappings by environment variable using JSON notation:
|
||||||
|
# ZITADEL_INTERNALAUTHZ_ROLEPERMISSIONMAPPINGS='[{"role": "IAM_OWNER", "permissions": ["iam.read", "iam.write"]}]'
|
||||||
|
# Beware that if you configure the RolePermissionMappings by environment variable, all the default RolePermissionMappings are lost.
|
||||||
RolePermissionMappings:
|
RolePermissionMappings:
|
||||||
- Role: "SYSTEM_OWNER"
|
- Role: "SYSTEM_OWNER"
|
||||||
Permissions:
|
Permissions:
|
||||||
|
35
cmd/hooks/complex.go
Normal file
35
cmd/hooks/complex.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package hooks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SliceTypeStringDecode[T any](from, to reflect.Value) (any, error) {
|
||||||
|
into := make([]T, 0)
|
||||||
|
return complexTypeStringDecodeHook(from, to, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapTypeStringDecode[K ~string | ~int, V any](from, to reflect.Value) (any, error) {
|
||||||
|
into := make(map[K]V, 0)
|
||||||
|
return complexTypeStringDecodeHook(from, to, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapHTTPHeaderStringDecode(from, to reflect.Value) (any, error) {
|
||||||
|
into := http.Header{}
|
||||||
|
return complexTypeStringDecodeHook(from, to, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
func complexTypeStringDecodeHook(from, to reflect.Value, out any) (any, error) {
|
||||||
|
fromInterface := from.Interface()
|
||||||
|
if to.Type() != reflect.TypeOf(out) {
|
||||||
|
return fromInterface, nil
|
||||||
|
}
|
||||||
|
data, ok := fromInterface.(string)
|
||||||
|
if !ok {
|
||||||
|
return fromInterface, nil
|
||||||
|
}
|
||||||
|
err := json.Unmarshal([]byte(data), &out)
|
||||||
|
return out, err
|
||||||
|
}
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/cmd/encryption"
|
"github.com/zitadel/zitadel/cmd/encryption"
|
||||||
"github.com/zitadel/zitadel/cmd/systemapi"
|
"github.com/zitadel/zitadel/cmd/hooks"
|
||||||
"github.com/zitadel/zitadel/internal/actions"
|
"github.com/zitadel/zitadel/internal/actions"
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/api/oidc"
|
"github.com/zitadel/zitadel/internal/api/oidc"
|
||||||
@ -47,7 +47,7 @@ type Config struct {
|
|||||||
Login login.Config
|
Login login.Config
|
||||||
WebAuthNName string
|
WebAuthNName string
|
||||||
Telemetry *handlers.TelemetryPusherConfig
|
Telemetry *handlers.TelemetryPusherConfig
|
||||||
SystemAPIUsers systemapi.Users
|
SystemAPIUsers map[string]*authz.SystemAPIUser
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitProjections struct {
|
type InitProjections struct {
|
||||||
@ -70,6 +70,7 @@ func MustNewConfig(v *viper.Viper) *Config {
|
|||||||
hook.EnumHookFunc(domain.FeatureString),
|
hook.EnumHookFunc(domain.FeatureString),
|
||||||
hook.EnumHookFunc(authz.MemberTypeString),
|
hook.EnumHookFunc(authz.MemberTypeString),
|
||||||
actions.HTTPConfigDecodeHook,
|
actions.HTTPConfigDecodeHook,
|
||||||
|
hooks.MapTypeStringDecode[string, *authz.SystemAPIUser],
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
logging.OnError(err).Fatal("unable to read default config")
|
logging.OnError(err).Fatal("unable to read default config")
|
||||||
@ -127,7 +128,6 @@ func MustNewSteps(v *viper.Viper) *Steps {
|
|||||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||||
mapstructure.StringToSliceHookFunc(","),
|
mapstructure.StringToSliceHookFunc(","),
|
||||||
hook.EnumHookFunc(domain.FeatureString),
|
hook.EnumHookFunc(domain.FeatureString),
|
||||||
systemapi.UsersDecodeHook,
|
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
logging.OnError(err).Fatal("unable to read steps")
|
logging.OnError(err).Fatal("unable to read steps")
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/zitadel/logging"
|
"github.com/zitadel/logging"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/cmd/encryption"
|
"github.com/zitadel/zitadel/cmd/encryption"
|
||||||
"github.com/zitadel/zitadel/cmd/systemapi"
|
"github.com/zitadel/zitadel/cmd/hooks"
|
||||||
"github.com/zitadel/zitadel/internal/actions"
|
"github.com/zitadel/zitadel/internal/actions"
|
||||||
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
|
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
|
||||||
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
||||||
@ -61,7 +61,7 @@ type Config struct {
|
|||||||
EncryptionKeys *encryption.EncryptionKeyConfig
|
EncryptionKeys *encryption.EncryptionKeyConfig
|
||||||
DefaultInstance command.InstanceSetup
|
DefaultInstance command.InstanceSetup
|
||||||
AuditLogRetention time.Duration
|
AuditLogRetention time.Duration
|
||||||
SystemAPIUsers systemapi.Users
|
SystemAPIUsers map[string]*internal_authz.SystemAPIUser
|
||||||
CustomerPortal string
|
CustomerPortal string
|
||||||
Machine *id.Config
|
Machine *id.Config
|
||||||
Actions *actions.Config
|
Actions *actions.Config
|
||||||
@ -84,6 +84,12 @@ func MustNewConfig(v *viper.Viper) *Config {
|
|||||||
|
|
||||||
err := v.Unmarshal(config,
|
err := v.Unmarshal(config,
|
||||||
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
||||||
|
hooks.SliceTypeStringDecode[*domain.CustomMessageText],
|
||||||
|
hooks.SliceTypeStringDecode[*command.SetQuota],
|
||||||
|
hooks.SliceTypeStringDecode[internal_authz.RoleMapping],
|
||||||
|
hooks.MapTypeStringDecode[string, *internal_authz.SystemAPIUser],
|
||||||
|
hooks.MapTypeStringDecode[domain.Feature, any],
|
||||||
|
hooks.MapHTTPHeaderStringDecode,
|
||||||
hook.Base64ToBytesHookFunc(),
|
hook.Base64ToBytesHookFunc(),
|
||||||
hook.TagToLanguageHookFunc(),
|
hook.TagToLanguageHookFunc(),
|
||||||
mapstructure.StringToTimeDurationHookFunc(),
|
mapstructure.StringToTimeDurationHookFunc(),
|
||||||
@ -91,7 +97,6 @@ func MustNewConfig(v *viper.Viper) *Config {
|
|||||||
mapstructure.StringToSliceHookFunc(","),
|
mapstructure.StringToSliceHookFunc(","),
|
||||||
database.DecodeHook,
|
database.DecodeHook,
|
||||||
actions.HTTPConfigDecodeHook,
|
actions.HTTPConfigDecodeHook,
|
||||||
systemapi.UsersDecodeHook,
|
|
||||||
hook.EnumHookFunc(domain.FeatureString),
|
hook.EnumHookFunc(domain.FeatureString),
|
||||||
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
||||||
)),
|
)),
|
||||||
|
@ -1,44 +1,106 @@
|
|||||||
package start
|
package start
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/zitadel/logging"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/actions"
|
"github.com/zitadel/zitadel/internal/actions"
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/command"
|
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMustNewConfig(t *testing.T) {
|
func TestMustNewConfig(t *testing.T) {
|
||||||
|
encodedKey := "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6aStGRlNKTDdmNXl3NEtUd3pnTQpQMzRlUEd5Y20vTStrVDBNN1Y0Q2d4NVYzRWFESXZUUUtUTGZCYUVCNDV6YjlMdGpJWHpEdzByWFJvUzJoTzZ0CmgrQ1lRQ3ozS0N2aDA5QzBJenhaaUIySVMzSC9hVCs1Qng5RUZZK3ZuQWtaamNjYnlHNVlOUnZtdE9sbnZJZUkKSDdxWjB0RXdrUGZGNUdFWk5QSlB0bXkzVUdWN2lvZmRWUVMxeFJqNzMrYU13NXJ2SDREOElkeWlBQzNWZWtJYgpwdDBWajBTVVgzRHdLdG9nMzM3QnpUaVBrM2FYUkYwc2JGaFFvcWRKUkk4TnFnWmpDd2pxOXlmSTV0eXhZc3duCitKR3pIR2RIdlczaWRPRGxtd0V0NUsycGFzaVJJV0syT0dmcSt3MEVjbHRRSGFidXFFUGdabG1oQ2tSZE5maXgKQndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
|
||||||
|
decodedKey, err := base64.StdEncoding.DecodeString(encodedKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
yaml string
|
yaml string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want *Config
|
want func(*testing.T, *Config)
|
||||||
}{{
|
}{{
|
||||||
|
name: "actions deny list ok",
|
||||||
|
args: args{
|
||||||
|
yaml: `
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList:
|
||||||
|
- localhost
|
||||||
|
- 127.0.0.1
|
||||||
|
- foobar
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.Actions.HTTP.DenyList, []actions.AddressChecker{
|
||||||
|
&actions.DomainChecker{Domain: "localhost"},
|
||||||
|
&actions.IPChecker{IP: net.ParseIP("127.0.0.1")},
|
||||||
|
&actions.DomainChecker{Domain: "foobar"}})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "actions deny list string ok",
|
||||||
|
args: args{
|
||||||
|
yaml: `
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: localhost,127.0.0.1,foobar
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.Actions.HTTP.DenyList, []actions.AddressChecker{
|
||||||
|
&actions.DomainChecker{Domain: "localhost"},
|
||||||
|
&actions.IPChecker{IP: net.ParseIP("127.0.0.1")},
|
||||||
|
&actions.DomainChecker{Domain: "foobar"}})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
name: "features ok",
|
name: "features ok",
|
||||||
args: args{yaml: `
|
args: args{yaml: `
|
||||||
DefaultInstance:
|
DefaultInstance:
|
||||||
Features:
|
Features:
|
||||||
- FeatureLoginDefaultOrg: true
|
- FeatureLoginDefaultOrg: true
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
`},
|
`},
|
||||||
want: &Config{
|
want: func(t *testing.T, config *Config) {
|
||||||
DefaultInstance: command.InstanceSetup{
|
assert.Equal(t, config.DefaultInstance.Features, map[domain.Feature]any{
|
||||||
Features: map[domain.Feature]any{
|
domain.FeatureLoginDefaultOrg: true,
|
||||||
domain.FeatureLoginDefaultOrg: true,
|
})
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "membership types ok",
|
name: "features string ok",
|
||||||
|
args: args{yaml: `
|
||||||
|
DefaultInstance:
|
||||||
|
Features: >
|
||||||
|
[{"featureLoginDefaultOrg": true}]
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.DefaultInstance.Features, map[domain.Feature]any{
|
||||||
|
domain.FeatureLoginDefaultOrg: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "system api users ok",
|
||||||
args: args{yaml: `
|
args: args{yaml: `
|
||||||
SystemAPIUsers:
|
SystemAPIUsers:
|
||||||
- superuser:
|
- superuser:
|
||||||
@ -46,9 +108,14 @@ SystemAPIUsers:
|
|||||||
- MemberType: System
|
- MemberType: System
|
||||||
- MemberType: Organization
|
- MemberType: Organization
|
||||||
- MemberType: IAM
|
- MemberType: IAM
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
`},
|
`},
|
||||||
want: &Config{
|
want: func(t *testing.T, config *Config) {
|
||||||
SystemAPIUsers: map[string]*authz.SystemAPIUser{
|
assert.Equal(t, config.SystemAPIUsers, map[string]*authz.SystemAPIUser{
|
||||||
"superuser": {
|
"superuser": {
|
||||||
Memberships: authz.Memberships{{
|
Memberships: authz.Memberships{{
|
||||||
MemberType: authz.MemberTypeSystem,
|
MemberType: authz.MemberTypeSystem,
|
||||||
@ -58,27 +125,121 @@ SystemAPIUsers:
|
|||||||
MemberType: authz.MemberTypeIAM,
|
MemberType: authz.MemberTypeIAM,
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "system api users string ok",
|
||||||
|
args: args{yaml: fmt.Sprintf(`
|
||||||
|
SystemAPIUsers: >
|
||||||
|
{"systemuser": {"path": "/path/to/superuser/key.pem"}, "systemuser2": {"keyData": "%s"}}
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`, encodedKey)},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.SystemAPIUsers, map[string]*authz.SystemAPIUser{
|
||||||
|
"systemuser": {
|
||||||
|
Path: "/path/to/superuser/key.pem",
|
||||||
|
},
|
||||||
|
"systemuser2": {
|
||||||
|
KeyData: decodedKey,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "headers ok",
|
||||||
|
args: args{yaml: `
|
||||||
|
Telemetry:
|
||||||
|
Headers:
|
||||||
|
single-value: single-value
|
||||||
|
multi-value:
|
||||||
|
- multi-value1
|
||||||
|
- multi-value2
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.Telemetry.Headers, http.Header{
|
||||||
|
"single-value": []string{"single-value"},
|
||||||
|
"multi-value": []string{"multi-value1", "multi-value2"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "headers string ok",
|
||||||
|
args: args{yaml: `
|
||||||
|
Telemetry:
|
||||||
|
Headers: >
|
||||||
|
{"single-value": "single-value", "multi-value": ["multi-value1", "multi-value2"]}
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.Telemetry.Headers, http.Header{
|
||||||
|
"single-value": []string{"single-value"},
|
||||||
|
"multi-value": []string{"multi-value1", "multi-value2"},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "message texts ok",
|
||||||
|
args: args{yaml: `
|
||||||
|
DefaultInstance:
|
||||||
|
MessageTexts:
|
||||||
|
- MessageTextType: InitCode
|
||||||
|
Title: foo
|
||||||
|
- MessageTextType: PasswordReset
|
||||||
|
Greeting: bar
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.DefaultInstance.MessageTexts, []*domain.CustomMessageText{{
|
||||||
|
MessageTextType: "InitCode",
|
||||||
|
Title: "foo",
|
||||||
|
}, {
|
||||||
|
MessageTextType: "PasswordReset",
|
||||||
|
Greeting: "bar",
|
||||||
|
}})
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
name: "message texts string ok",
|
||||||
|
args: args{yaml: `
|
||||||
|
DefaultInstance:
|
||||||
|
MessageTexts: >
|
||||||
|
[{"messageTextType": "InitCode", "title": "foo"}, {"messageTextType": "PasswordReset", "greeting": "bar"}]
|
||||||
|
Log:
|
||||||
|
Level: info
|
||||||
|
Actions:
|
||||||
|
HTTP:
|
||||||
|
DenyList: []
|
||||||
|
`},
|
||||||
|
want: func(t *testing.T, config *Config) {
|
||||||
|
assert.Equal(t, config.DefaultInstance.MessageTexts, []*domain.CustomMessageText{{
|
||||||
|
MessageTextType: "InitCode",
|
||||||
|
Title: "foo",
|
||||||
|
}, {
|
||||||
|
MessageTextType: "PasswordReset",
|
||||||
|
Greeting: "bar",
|
||||||
|
}})
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
v := viper.New()
|
v := viper.New()
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
err := v.ReadConfig(strings.NewReader(`Log:
|
require.NoError(t, v.ReadConfig(strings.NewReader(tt.args.yaml)))
|
||||||
Level: info
|
|
||||||
Actions:
|
|
||||||
HTTP:
|
|
||||||
DenyList: []
|
|
||||||
` + tt.args.yaml))
|
|
||||||
require.NoError(t, err)
|
|
||||||
tt.want.Log = &logging.Config{Level: "info"}
|
|
||||||
tt.want.Actions = &actions.Config{HTTP: actions.HTTPConfig{DenyList: []actions.AddressChecker{}}}
|
|
||||||
require.NoError(t, tt.want.Log.SetLogger())
|
|
||||||
got := MustNewConfig(v)
|
got := MustNewConfig(v)
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
tt.want(t, got)
|
||||||
t.Errorf("MustNewConfig() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package systemapi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Users map[string]*authz.SystemAPIUser
|
|
||||||
|
|
||||||
func UsersDecodeHook(from, to reflect.Value) (any, error) {
|
|
||||||
if to.Type() != reflect.TypeOf(Users{}) {
|
|
||||||
return from.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data, ok := from.Interface().(string)
|
|
||||||
if !ok {
|
|
||||||
return from.Interface(), nil
|
|
||||||
}
|
|
||||||
users := make(Users)
|
|
||||||
err := json.Unmarshal([]byte(data), &users)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return users, nil
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package actions
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
@ -41,12 +42,18 @@ func HTTPConfigDecodeHook(from, to reflect.Value) (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := HTTPConfig{
|
c := HTTPConfig{
|
||||||
DenyList: make([]AddressChecker, len(config.DenyList)),
|
DenyList: make([]AddressChecker, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, entry := range config.DenyList {
|
for _, unsplit := range config.DenyList {
|
||||||
if c.DenyList[i], err = parseDenyListEntry(entry); err != nil {
|
for _, split := range strings.Split(unsplit, ",") {
|
||||||
return nil, err
|
parsed, parseErr := parseDenyListEntry(split)
|
||||||
|
if parseErr != nil {
|
||||||
|
return nil, parseErr
|
||||||
|
}
|
||||||
|
if parsed != nil {
|
||||||
|
c.DenyList = append(c.DenyList, parsed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user