mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
fix(setup): decode complex config strings (#7854)
* fix(setup): decode complex config strings * decode complex types before string lists * lint * fix hasher env docs * hasher defaults * cleanup * cleanup --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
parent
9950cafafc
commit
6c0e7c402d
@ -12,13 +12,14 @@ import (
|
||||
"github.com/zitadel/zitadel/cmd/encryption"
|
||||
"github.com/zitadel/zitadel/cmd/hooks"
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
internal_authz "github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/oidc"
|
||||
"github.com/zitadel/zitadel/internal/api/ui/login"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/hook"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/notification/handlers"
|
||||
@ -29,7 +30,7 @@ import (
|
||||
type Config struct {
|
||||
Database database.Config
|
||||
SystemDefaults systemdefaults.SystemDefaults
|
||||
InternalAuthZ authz.Config
|
||||
InternalAuthZ internal_authz.Config
|
||||
ExternalDomain string
|
||||
ExternalPort uint16
|
||||
ExternalSecure bool
|
||||
@ -46,7 +47,7 @@ type Config struct {
|
||||
Login login.Config
|
||||
WebAuthNName string
|
||||
Telemetry *handlers.TelemetryPusherConfig
|
||||
SystemAPIUsers map[string]*authz.SystemAPIUser
|
||||
SystemAPIUsers map[string]*internal_authz.SystemAPIUser
|
||||
}
|
||||
|
||||
type InitProjections struct {
|
||||
@ -60,16 +61,18 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
config := new(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.MapHTTPHeaderStringDecode,
|
||||
database.DecodeHook,
|
||||
actions.HTTPConfigDecodeHook,
|
||||
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
database.DecodeHook,
|
||||
hook.EnumHookFunc(authz.MemberTypeString),
|
||||
actions.HTTPConfigDecodeHook,
|
||||
hooks.MapTypeStringDecode[string, *authz.SystemAPIUser],
|
||||
hooks.SliceTypeStringDecode[authz.RoleMapping],
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read default config")
|
||||
|
245
cmd/setup/config_test.go
Normal file
245
cmd/setup/config_test.go
Normal file
@ -0,0 +1,245 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"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 func(*testing.T, *Config)
|
||||
}{{
|
||||
name: "features ok",
|
||||
args: args{yaml: `
|
||||
DefaultInstance:
|
||||
Features:
|
||||
LoginDefaultOrg: true
|
||||
LegacyIntrospection: true
|
||||
TriggerIntrospectionProjections: true
|
||||
UserSchema: true
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.DefaultInstance.Features, &command.InstanceFeatures{
|
||||
LoginDefaultOrg: gu.Ptr(true),
|
||||
LegacyIntrospection: gu.Ptr(true),
|
||||
TriggerIntrospectionProjections: gu.Ptr(true),
|
||||
UserSchema: gu.Ptr(true),
|
||||
})
|
||||
},
|
||||
}, {
|
||||
name: "system api users ok",
|
||||
args: args{yaml: `
|
||||
SystemAPIUsers:
|
||||
- superuser:
|
||||
Memberships:
|
||||
- MemberType: System
|
||||
- MemberType: Organization
|
||||
- MemberType: IAM
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.SystemAPIUsers, map[string]*authz.SystemAPIUser{
|
||||
"superuser": {
|
||||
Memberships: authz.Memberships{{
|
||||
MemberType: authz.MemberTypeSystem,
|
||||
}, {
|
||||
MemberType: authz.MemberTypeOrganization,
|
||||
}, {
|
||||
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",
|
||||
}})
|
||||
},
|
||||
}, {
|
||||
name: "roles ok",
|
||||
args: args{yaml: `
|
||||
InternalAuthZ:
|
||||
RolePermissionMappings:
|
||||
- Role: IAM_OWNER
|
||||
Permissions:
|
||||
- iam.write
|
||||
- Role: ORG_OWNER
|
||||
Permissions:
|
||||
- org.write
|
||||
- org.read
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.InternalAuthZ, authz.Config{
|
||||
RolePermissionMappings: []authz.RoleMapping{
|
||||
{Role: "IAM_OWNER", Permissions: []string{"iam.write"}},
|
||||
{Role: "ORG_OWNER", Permissions: []string{"org.write", "org.read"}},
|
||||
},
|
||||
})
|
||||
},
|
||||
}, {
|
||||
name: "roles string ok",
|
||||
args: args{yaml: `
|
||||
InternalAuthZ:
|
||||
RolePermissionMappings: >
|
||||
[{"role": "IAM_OWNER", "permissions": ["iam.write"]}, {"role": "ORG_OWNER", "permissions": ["org.write", "org.read"]}]
|
||||
Log:
|
||||
Level: info
|
||||
Actions:
|
||||
HTTP:
|
||||
DenyList: []
|
||||
`},
|
||||
want: func(t *testing.T, config *Config) {
|
||||
assert.Equal(t, config.InternalAuthZ, authz.Config{
|
||||
RolePermissionMappings: []authz.RoleMapping{
|
||||
{Role: "IAM_OWNER", Permissions: []string{"iam.write"}},
|
||||
{Role: "ORG_OWNER", Permissions: []string{"org.write", "org.read"}},
|
||||
},
|
||||
})
|
||||
},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := viper.New()
|
||||
v.SetConfigType("yaml")
|
||||
require.NoError(t, v.ReadConfig(strings.NewReader(tt.args.yaml)))
|
||||
got := MustNewConfig(v)
|
||||
tt.want(t, got)
|
||||
})
|
||||
}
|
||||
}
|
@ -85,19 +85,19 @@ 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,
|
||||
database.DecodeHook,
|
||||
actions.HTTPConfigDecodeHook,
|
||||
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
||||
hooks.MapTypeStringDecode[domain.Feature, any],
|
||||
hooks.SliceTypeStringDecode[*command.SetQuota],
|
||||
hook.Base64ToBytesHookFunc(),
|
||||
hook.TagToLanguageHookFunc(),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
database.DecodeHook,
|
||||
actions.HTTPConfigDecodeHook,
|
||||
hook.EnumHookFunc(internal_authz.MemberTypeString),
|
||||
)),
|
||||
)
|
||||
logging.OnError(err).Fatal("unable to read config")
|
||||
|
Loading…
Reference in New Issue
Block a user