mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 15:35:10 +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:
|
||||
Level: info # ZITADEL_LOG_LEVEL
|
||||
Formatter:
|
||||
Format: text # ZITADEL_LOG_LEVEL
|
||||
Format: text # ZITADEL_LOG_FORMATTER_FORMAT
|
||||
|
||||
# Exposes metrics on /debug/metrics
|
||||
Metrics:
|
||||
@ -29,7 +29,7 @@ Telemetry:
|
||||
# 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.
|
||||
# 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.
|
||||
# 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.
|
||||
@ -40,7 +40,9 @@ Telemetry:
|
||||
Endpoints:
|
||||
- https://httpbin.org/post
|
||||
# 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"
|
||||
# multi-value:
|
||||
# - "multi-value-1"
|
||||
@ -85,7 +87,7 @@ HTTP2HostHeader: ":authority" # ZITADEL_HTTP2HOSTHEADER
|
||||
# Header name of HTTP1 calls from which the instance will be matched
|
||||
HTTP1HostHeader: "host" # ZITADEL_HTTP1HOSTHEADER
|
||||
|
||||
WebAuthNName: ZITADEL # ZITADEL_WEBAUTHN_NAME
|
||||
WebAuthNName: ZITADEL # ZITADEL_WEBAUTHNNAME
|
||||
|
||||
Database:
|
||||
# ZITADEL manages three database connection pools.
|
||||
@ -170,7 +172,7 @@ Machine:
|
||||
Enabled: true # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_ENABLED
|
||||
Url: "http://metadata.google.internal/computeMetadata/v1/instance/id" # ZITADEL_MACHINE_IDENTIFICATION_WEBHOOK_URL
|
||||
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
|
||||
# Webhook:
|
||||
@ -205,7 +207,7 @@ Projections:
|
||||
# Time interval between scheduled projections
|
||||
RequeueEvery: 60s # ZITADEL_PROJECTIONS_REQUEUEEVERY
|
||||
# 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
|
||||
MaxFailureCount: 5 # ZITADEL_PROJECTIONS_MAXFAILURECOUNT
|
||||
# Limit of returned events per query
|
||||
@ -378,28 +380,28 @@ Console:
|
||||
EncryptionKeys:
|
||||
DomainVerification:
|
||||
EncryptionKeyID: "domainVerificationKey" # ZITADEL_ENCRYPTIONKEYS_DOMAINVERIFICATION_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_DOMAINVERIFICATION_DECRYPTIONKEYIDS (comma separated list)
|
||||
IDPConfig:
|
||||
EncryptionKeyID: "idpConfigKey" # ZITADEL_ENCRYPTIONKEYS_IDPCONFIG_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_IDPCONFIG_DECRYPTIONKEYIDS (comma separated list)
|
||||
OIDC:
|
||||
EncryptionKeyID: "oidcKey" # ZITADEL_ENCRYPTIONKEYS_OIDC_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_OIDC_DECRYPTIONKEYIDS (comma separated list)
|
||||
SAML:
|
||||
EncryptionKeyID: "samlKey" # ZITADEL_ENCRYPTIONKEYS_SAML_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SAML_DECRYPTIONKEYIDS (comma separated list)
|
||||
OTP:
|
||||
EncryptionKeyID: "otpKey" # ZITADEL_ENCRYPTIONKEYS_OTP_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_OTP_DECRYPTIONKEYIDS (comma separated list)
|
||||
SMS:
|
||||
EncryptionKeyID: "smsKey" # ZITADEL_ENCRYPTIONKEYS_SMS_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SMS_DECRYPTIONKEYIDS (comma separated list)
|
||||
SMTP:
|
||||
EncryptionKeyID: "smtpKey" # ZITADEL_ENCRYPTIONKEYS_SMTP_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_SMTP_DECRYPTIONKEYIDS (comma separated list)
|
||||
User:
|
||||
EncryptionKeyID: "userKey" # ZITADEL_ENCRYPTIONKEYS_USER_ENCRYPTIONKEYID
|
||||
DecryptionKeyIDs:
|
||||
DecryptionKeyIDs: # ZITADEL_ENCRYPTIONKEYS_USER_DECRYPTIONKEYIDS (comma separated list)
|
||||
CSRFCookieKeyID: "csrfCookieKey" # ZITADEL_ENCRYPTIONKEYS_CSRFCOOKIEKEYID
|
||||
UserAgentCookieKeyID: "userAgentCookieKey" # ZITADEL_ENCRYPTIONKEYS_USERAGENTCOOKIEKEYID
|
||||
|
||||
@ -426,6 +428,8 @@ SystemAPIUsers:
|
||||
# - superuser2:
|
||||
# # 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
|
||||
# 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
|
||||
SystemDefaults:
|
||||
@ -457,13 +461,13 @@ SystemDefaults:
|
||||
# Threads: 4 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_THREADS
|
||||
|
||||
# Hasher:
|
||||
# Algorithm: "scrypt"
|
||||
# Cost: 15
|
||||
# Algorithm: "scrypt" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
||||
# Cost: 15 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_COST
|
||||
|
||||
# Hasher:
|
||||
# Algorithm: "pbkdf2"
|
||||
# Rounds: 290000
|
||||
# Hash: "sha256" # Can be "sha1", "sha224", "sha256", "sha384" or "sha512"
|
||||
# Algorithm: "pbkdf2" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ALGORITHM
|
||||
# Rounds: 290000 # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_ROUNDS
|
||||
# Hash: "sha256" # Can be "sha1", "sha224", "sha256", "sha384" or "sha512" # ZITADEL_SYSTEMDEFAULTS_PASSWORDHASHER_HASHER_HASH
|
||||
|
||||
# Verifiers enable the possibility of verifying
|
||||
# passwords that are previously hashed using another
|
||||
@ -509,7 +513,7 @@ SystemDefaults:
|
||||
Actions:
|
||||
HTTP:
|
||||
# Wildcard sub domains are currently unsupported
|
||||
DenyList:
|
||||
DenyList: # ZITADEL_ACTIONS_HTTP_DENYLIST (comma separated list)
|
||||
- localhost
|
||||
- "127.0.0.1"
|
||||
|
||||
@ -727,6 +731,9 @@ DefaultInstance:
|
||||
From: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROM
|
||||
FromName: # ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_FROMNAME
|
||||
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:
|
||||
- MessageTextType: InitCode
|
||||
Language: de
|
||||
@ -857,7 +864,9 @@ DefaultInstance:
|
||||
|
||||
# "actions.all.runs.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"
|
||||
# # From defines the starting time from which the current quota period is calculated.
|
||||
# # This is relevant for querying the current usage.
|
||||
@ -884,6 +893,9 @@ DefaultInstance:
|
||||
AuditLogRetention: 0s # ZITADEL_AUDITLOGRETENTION
|
||||
|
||||
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:
|
||||
- Role: "SYSTEM_OWNER"
|
||||
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/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/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/oidc"
|
||||
@ -47,7 +47,7 @@ type Config struct {
|
||||
Login login.Config
|
||||
WebAuthNName string
|
||||
Telemetry *handlers.TelemetryPusherConfig
|
||||
SystemAPIUsers systemapi.Users
|
||||
SystemAPIUsers map[string]*authz.SystemAPIUser
|
||||
}
|
||||
|
||||
type InitProjections struct {
|
||||
@ -70,6 +70,7 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
hook.EnumHookFunc(domain.FeatureString),
|
||||
hook.EnumHookFunc(authz.MemberTypeString),
|
||||
actions.HTTPConfigDecodeHook,
|
||||
hooks.MapTypeStringDecode[string, *authz.SystemAPIUser],
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read default config")
|
||||
@ -127,7 +128,6 @@ func MustNewSteps(v *viper.Viper) *Steps {
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
hook.EnumHookFunc(domain.FeatureString),
|
||||
systemapi.UsersDecodeHook,
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read steps")
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/cmd/encryption"
|
||||
"github.com/zitadel/zitadel/cmd/systemapi"
|
||||
"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"
|
||||
@ -61,7 +61,7 @@ type Config struct {
|
||||
EncryptionKeys *encryption.EncryptionKeyConfig
|
||||
DefaultInstance command.InstanceSetup
|
||||
AuditLogRetention time.Duration
|
||||
SystemAPIUsers systemapi.Users
|
||||
SystemAPIUsers map[string]*internal_authz.SystemAPIUser
|
||||
CustomerPortal string
|
||||
Machine *id.Config
|
||||
Actions *actions.Config
|
||||
@ -84,6 +84,12 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
|
||||
err := v.Unmarshal(config,
|
||||
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.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
@ -91,7 +97,6 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
database.DecodeHook,
|
||||
actions.HTTPConfigDecodeHook,
|
||||
systemapi.UsersDecodeHook,
|
||||
hook.EnumHookFunc(domain.FeatureString),
|
||||
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
||||
)),
|
||||
|
@ -1,44 +1,106 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
func TestMustNewConfig(t *testing.T) {
|
||||
encodedKey := "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6aStGRlNKTDdmNXl3NEtUd3pnTQpQMzRlUEd5Y20vTStrVDBNN1Y0Q2d4NVYzRWFESXZUUUtUTGZCYUVCNDV6YjlMdGpJWHpEdzByWFJvUzJoTzZ0CmgrQ1lRQ3ozS0N2aDA5QzBJenhaaUIySVMzSC9hVCs1Qng5RUZZK3ZuQWtaamNjYnlHNVlOUnZtdE9sbnZJZUkKSDdxWjB0RXdrUGZGNUdFWk5QSlB0bXkzVUdWN2lvZmRWUVMxeFJqNzMrYU13NXJ2SDREOElkeWlBQzNWZWtJYgpwdDBWajBTVVgzRHdLdG9nMzM3QnpUaVBrM2FYUkYwc2JGaFFvcWRKUkk4TnFnWmpDd2pxOXlmSTV0eXhZc3duCitKR3pIR2RIdlczaWRPRGxtd0V0NUsycGFzaVJJV0syT0dmcSt3MEVjbHRRSGFidXFFUGdabG1oQ2tSZE5maXgKQndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
|
||||
decodedKey, err := base64.StdEncoding.DecodeString(encodedKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
type args struct {
|
||||
yaml string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
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",
|
||||
args: args{yaml: `
|
||||
DefaultInstance:
|
||||
Features:
|
||||
- FeatureLoginDefaultOrg: true
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: &Config{
|
||||
DefaultInstance: command.InstanceSetup{
|
||||
Features: map[domain.Feature]any{
|
||||
domain.FeatureLoginDefaultOrg: true,
|
||||
},
|
||||
},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.DefaultInstance.Features, map[domain.Feature]any{
|
||||
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: `
|
||||
SystemAPIUsers:
|
||||
- superuser:
|
||||
@ -46,9 +108,14 @@ SystemAPIUsers:
|
||||
- MemberType: System
|
||||
- MemberType: Organization
|
||||
- MemberType: IAM
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: &Config{
|
||||
SystemAPIUsers: map[string]*authz.SystemAPIUser{
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.SystemAPIUsers, map[string]*authz.SystemAPIUser{
|
||||
"superuser": {
|
||||
Memberships: authz.Memberships{{
|
||||
MemberType: authz.MemberTypeSystem,
|
||||
@ -58,27 +125,121 @@ SystemAPIUsers:
|
||||
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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := viper.New()
|
||||
v.SetConfigType("yaml")
|
||||
err := v.ReadConfig(strings.NewReader(`Log:
|
||||
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())
|
||||
require.NoError(t, v.ReadConfig(strings.NewReader(tt.args.yaml)))
|
||||
got := MustNewConfig(v)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MustNewConfig() = %v, want %v", got, tt.want)
|
||||
}
|
||||
tt.want(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 (
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -41,12 +42,18 @@ func HTTPConfigDecodeHook(from, to reflect.Value) (interface{}, error) {
|
||||
}
|
||||
|
||||
c := HTTPConfig{
|
||||
DenyList: make([]AddressChecker, len(config.DenyList)),
|
||||
DenyList: make([]AddressChecker, 0),
|
||||
}
|
||||
|
||||
for i, entry := range config.DenyList {
|
||||
if c.DenyList[i], err = parseDenyListEntry(entry); err != nil {
|
||||
return nil, err
|
||||
for _, unsplit := range config.DenyList {
|
||||
for _, split := range strings.Split(unsplit, ",") {
|
||||
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