diff --git a/backend/v3/storage/database/events_testing/id_provider_instance_test.go b/backend/v3/storage/database/events_testing/id_provider_instance_test.go index bfabf3bfd..a4964f4d4 100644 --- a/backend/v3/storage/database/events_testing/id_provider_instance_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_instance_test.go @@ -3,6 +3,9 @@ package events_test import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "testing" "time" @@ -14,6 +17,7 @@ import ( "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" + "github.com/zitadel/zitadel/internal/crypto" zitadel_internal_domain "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/pkg/grpc/admin" @@ -2012,7 +2016,7 @@ func TestServer_TestIDProviderInstanceReduces(t *testing.T) { ClientId: "clientID", TeamId: "teamIDteam", KeyId: "keyIDKeyId", - PrivateKey: []byte("privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: false, @@ -2064,7 +2068,7 @@ func TestServer_TestIDProviderInstanceReduces(t *testing.T) { ClientId: "clientID", TeamId: "teamIDteam", KeyId: "keyIDKeyId", - PrivateKey: []byte("privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: false, @@ -2095,7 +2099,7 @@ func TestServer_TestIDProviderInstanceReduces(t *testing.T) { ClientId: "new_clientID", TeamId: "new_teamID", KeyId: "new_kKeyId", - PrivateKey: []byte("new_privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"new_scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: true, @@ -2361,3 +2365,11 @@ func defaultInstanceLDAPRequest(name string) *admin.AddLDAPProviderRequest { }, } } + +func generatePrivateKeyPKCS8(t *testing.T) []byte { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + data, err := crypto.PrivateKeyToBytesPKCS8(privateKey) + require.NoError(t, err) + return data +} diff --git a/backend/v3/storage/database/events_testing/id_provider_org_test.go b/backend/v3/storage/database/events_testing/id_provider_org_test.go index cc12a131b..539f37198 100644 --- a/backend/v3/storage/database/events_testing/id_provider_org_test.go +++ b/backend/v3/storage/database/events_testing/id_provider_org_test.go @@ -1978,7 +1978,7 @@ func TestServer_TestIDProviderOrgReduces(t *testing.T) { ClientId: "clientID", TeamId: "teamIDteam", KeyId: "keyIDKeyId", - PrivateKey: []byte("privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: false, @@ -2030,7 +2030,7 @@ func TestServer_TestIDProviderOrgReduces(t *testing.T) { ClientId: "clientID", TeamId: "teamIDteam", KeyId: "keyIDKeyId", - PrivateKey: []byte("privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: false, @@ -2061,7 +2061,7 @@ func TestServer_TestIDProviderOrgReduces(t *testing.T) { ClientId: "new_clientID", TeamId: "new_teamID", KeyId: "new_kKeyId", - PrivateKey: []byte("new_privateKey"), + PrivateKey: generatePrivateKeyPKCS8(t), Scopes: []string{"new_scope"}, ProviderOptions: &idp_grpc.Options{ IsLinkingAllowed: true, diff --git a/internal/api/grpc/project/application.go b/internal/api/grpc/project/application.go index fc05013c5..29fbd4cd2 100644 --- a/internal/api/grpc/project/application.go +++ b/internal/api/grpc/project/application.go @@ -38,7 +38,10 @@ func AppConfigToPb(app *query.App) app_pb.AppConfig { if app.SAMLConfig != nil { return AppSAMLConfigToPb(app.SAMLConfig) } - return AppAPIConfigToPb(app.APIConfig) + if app.APIConfig != nil { + return AppAPIConfigToPb(app.APIConfig) + } + return nil } func AppOIDCConfigToPb(app *query.OIDCApp) *app_pb.App_OidcConfig { diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 1f6536ef8..419e0436e 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -12,6 +12,7 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/idp/providers/apple" "github.com/zitadel/zitadel/internal/idp/providers/saml" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/zerrors" @@ -1668,6 +1669,9 @@ func (c *Commands) prepareAddInstanceAppleProvider(a *instance.Aggregate, writeM if len(provider.PrivateKey) == 0 { return nil, zerrors.ThrowInvalidArgument(nil, "INST-GVD4n", "Errors.IDP.PrivateKeyMissing") } + if _, err := apple.BytesToPrivateKey(provider.PrivateKey); err != nil { + return nil, zerrors.ThrowInvalidArgument(err, "INST-Fk38d", "Errors.IDP.InvalidPrivateKey") + } return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { events, err := filter(ctx, writeModel.Query()) if err != nil { @@ -1713,6 +1717,11 @@ func (c *Commands) prepareUpdateInstanceAppleProvider(a *instance.Aggregate, wri if provider.KeyID = strings.TrimSpace(provider.KeyID); provider.KeyID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "INST-Gh4z2", "Errors.IDP.KeyIDMissing") } + if len(provider.PrivateKey) > 0 { + if _, err := apple.BytesToPrivateKey(provider.PrivateKey); err != nil { + return nil, zerrors.ThrowInvalidArgument(err, "INST-eWSDf", "Errors.IDP.InvalidPrivateKey") + } + } return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { events, err := filter(ctx, writeModel.Query()) if err != nil { diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 8e6b92bcf..a5c172b42 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -2,6 +2,9 @@ package command import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "errors" "testing" "time" @@ -4893,6 +4896,14 @@ func TestCommandSide_UpdateInstanceLDAPIDP(t *testing.T) { } } +var ( + privateKeyPKCS8 = func() []byte { + privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + data, _ := crypto.PrivateKeyToBytesPKCS8(privateKey) + return data + }() +) + func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { type fields struct { eventstore func(*testing.T) *eventstore.Eventstore @@ -4906,7 +4917,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { type res struct { id string want *domain.ObjectDetails - err func(error) bool + err error } tests := []struct { name string @@ -4925,9 +4936,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-jkn3w", "Errors.IDP.ClientIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-jkn3w", "Errors.IDP.ClientIDMissing"), }, }, { @@ -4943,9 +4952,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-Ffg32", "Errors.IDP.TeamIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-Ffg32", "Errors.IDP.TeamIDMissing"), }, }, { @@ -4962,13 +4969,11 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-GDjm5", "Errors.IDP.KeyIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-GDjm5", "Errors.IDP.KeyIDMissing"), }, }, { - "invalid privateKey", + "no privateKey", fields{ eventstore: expectEventstore(), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -4982,10 +4987,27 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-GVD4n", "Errors.IDP.PrivateKeyMissing")) + err: zerrors.ThrowInvalidArgument(nil, "INST-GVD4n", "Errors.IDP.PrivateKeyMissing"), + }, + }, + { + "invalid privateKey", + fields{ + eventstore: expectEventstore(), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + provider: AppleProvider{ + ClientID: "clientID", + TeamID: "teamID", + KeyID: "keyID", + PrivateKey: []byte("invalid"), }, }, + res{ + err: zerrors.ThrowInvalidArgument(crypto.ErrEmpty, "INST-Fk38d", "Errors.IDP.InvalidPrivateKey"), + }, }, { name: "ok", @@ -5003,7 +5025,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("privateKey"), + Crypted: privateKeyPKCS8, }, nil, idp.Options{}, @@ -5019,7 +5041,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { ClientID: "clientID", TeamID: "teamID", KeyID: "keyID", - PrivateKey: []byte("privateKey"), + PrivateKey: privateKeyPKCS8, }, }, res: res{ @@ -5043,7 +5065,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("privateKey"), + Crypted: privateKeyPKCS8, }, []string{"name", "email"}, idp.Options{ @@ -5064,7 +5086,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { ClientID: "clientID", TeamID: "teamID", KeyID: "keyID", - PrivateKey: []byte("privateKey"), + PrivateKey: privateKeyPKCS8, Scopes: []string{"name", "email"}, IDPOptions: idp.Options{ IsCreationAllowed: true, @@ -5088,12 +5110,7 @@ func TestCommandSide_AddInstanceAppleIDP(t *testing.T) { idpConfigEncryption: tt.fields.secretCrypto, } id, got, err := c.AddInstanceAppleProvider(tt.args.ctx, tt.args.provider) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } + assert.ErrorIs(t, err, tt.res.err) if tt.res.err == nil { assert.Equal(t, tt.res.id, id) assertObjectDetails(t, tt.res.want, got) @@ -5114,7 +5131,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { } type res struct { want *domain.ObjectDetails - err func(error) bool + err error } tests := []struct { name string @@ -5132,9 +5149,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-FRHBH", "Errors.IDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-FRHBH", "Errors.IDMissing"), }, }, { @@ -5148,9 +5163,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-SFm4l", "Errors.IDP.ClientIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-SFm4l", "Errors.IDP.ClientIDMissing"), }, }, { @@ -5166,9 +5179,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-SG34t", "Errors.IDP.TeamIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "INST-SG34t", "Errors.IDP.TeamIDMissing"), }, }, { @@ -5185,10 +5196,27 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-Gh4z2", "Errors.IDP.KeyIDMissing")) + err: zerrors.ThrowInvalidArgument(nil, "INST-Gh4z2", "Errors.IDP.KeyIDMissing"), + }, + }, + { + "invalid key", + fields{ + eventstore: expectEventstore(), + }, + args{ + ctx: authz.WithInstanceID(context.Background(), "instance1"), + id: "id1", + provider: AppleProvider{ + ClientID: "clientID", + TeamID: "teamID", + KeyID: "keyID", + PrivateKey: []byte("invalid"), }, }, + res{ + err: zerrors.ThrowInvalidArgument(nil, "INST-eWSDf", "Errors.IDP.InvalidPrivateKey"), + }, }, { name: "not found", @@ -5207,7 +5235,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { }, }, res: res{ - err: zerrors.IsNotFound, + err: zerrors.ThrowNotFound(nil, "INST-SG3bh", "Errors.IDPConfig.NotExisting"), }, }, { @@ -5282,7 +5310,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("newPrivateKey"), + Crypted: privateKeyPKCS8, }), idp.ChangeAppleScopes([]string{"name", "email"}), idp.ChangeAppleOptions(idp.OptionChanges{ @@ -5306,7 +5334,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { ClientID: "clientID2", TeamID: "teamID2", KeyID: "keyID2", - PrivateKey: []byte("newPrivateKey"), + PrivateKey: privateKeyPKCS8, Scopes: []string{"name", "email"}, IDPOptions: idp.Options{ IsCreationAllowed: true, @@ -5328,12 +5356,7 @@ func TestCommandSide_UpdateInstanceAppleIDP(t *testing.T) { idpConfigEncryption: tt.fields.secretCrypto, } got, err := c.UpdateInstanceAppleProvider(tt.args.ctx, tt.args.id, tt.args.provider) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } + assert.ErrorIs(t, err, tt.res.err) if tt.res.err == nil { assertObjectDetails(t, tt.res.want, got) } diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 20e7436ad..214cccb47 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -10,6 +10,7 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" + "github.com/zitadel/zitadel/internal/idp/providers/apple" "github.com/zitadel/zitadel/internal/idp/providers/saml" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/zerrors" @@ -1642,6 +1643,9 @@ func (c *Commands) prepareAddOrgAppleProvider(a *org.Aggregate, writeModel *OrgA if len(provider.PrivateKey) == 0 { return nil, zerrors.ThrowInvalidArgument(nil, "ORG-GVD4n", "Errors.IDP.PrivateKeyMissing") } + if _, err := apple.BytesToPrivateKey(provider.PrivateKey); err != nil { + return nil, zerrors.ThrowInvalidArgument(err, "ORG-Fk38d", "Errors.IDP.InvalidPrivateKey") + } return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { events, err := filter(ctx, writeModel.Query()) if err != nil { @@ -1687,6 +1691,11 @@ func (c *Commands) prepareUpdateOrgAppleProvider(a *org.Aggregate, writeModel *O if provider.KeyID = strings.TrimSpace(provider.KeyID); provider.KeyID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "ORG-Gh4z2", "Errors.IDP.KeyIDMissing") } + if len(provider.PrivateKey) > 0 { + if _, err := apple.BytesToPrivateKey(provider.PrivateKey); err != nil { + return nil, zerrors.ThrowInvalidArgument(err, "ORG-eWSDf", "Errors.IDP.InvalidPrivateKey") + } + } return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { events, err := filter(ctx, writeModel.Query()) if err != nil { diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 152b72a0a..45efa3a52 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -4966,7 +4966,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { type res struct { id string want *domain.ObjectDetails - err func(error) bool + err error } tests := []struct { name string @@ -4986,9 +4986,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-jkn3w", "Errors.IDP.ClientIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-jkn3w", "Errors.IDP.ClientIDMissing"), }, }, { @@ -5005,9 +5003,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-Ffg32", "Errors.IDP.TeamIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-Ffg32", "Errors.IDP.TeamIDMissing"), }, }, { @@ -5025,13 +5021,11 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-GDjm5", "Errors.IDP.KeyIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-GDjm5", "Errors.IDP.KeyIDMissing"), }, }, { - "invalid privateKey", + "no privateKey", fields{ eventstore: expectEventstore(), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), @@ -5046,10 +5040,28 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-GVD4n", "Errors.IDP.PrivateKeyMissing")) + err: zerrors.ThrowInvalidArgument(nil, "ORG-GVD4n", "Errors.IDP.PrivateKeyMissing"), + }, + }, + { + "invalid privateKey", + fields{ + eventstore: expectEventstore(), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), + }, + args{ + ctx: context.Background(), + resourceOwner: "org1", + provider: AppleProvider{ + ClientID: "clientID", + TeamID: "teamID", + KeyID: "keyID", + PrivateKey: []byte("invalid"), }, }, + res{ + err: zerrors.ThrowInvalidArgument(crypto.ErrEmpty, "ORG-Fk38d", "Errors.IDP.InvalidPrivateKey"), + }, }, { name: "ok", @@ -5067,7 +5079,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("privateKey"), + Crypted: privateKeyPKCS8, }, nil, idp.Options{}, @@ -5084,7 +5096,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { ClientID: "clientID", TeamID: "teamID", KeyID: "keyID", - PrivateKey: []byte("privateKey"), + PrivateKey: privateKeyPKCS8, }, }, res: res{ @@ -5108,7 +5120,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("privateKey"), + Crypted: privateKeyPKCS8, }, []string{"name", "email"}, idp.Options{ @@ -5130,7 +5142,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { ClientID: "clientID", TeamID: "teamID", KeyID: "keyID", - PrivateKey: []byte("privateKey"), + PrivateKey: privateKeyPKCS8, Scopes: []string{"name", "email"}, IDPOptions: idp.Options{ IsCreationAllowed: true, @@ -5154,12 +5166,7 @@ func TestCommandSide_AddOrgAppleIDP(t *testing.T) { idpConfigEncryption: tt.fields.secretCrypto, } id, got, err := c.AddOrgAppleProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.provider) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } + assert.ErrorIs(t, err, tt.res.err) if tt.res.err == nil { assert.Equal(t, tt.res.id, id) assertObjectDetails(t, tt.res.want, got) @@ -5181,7 +5188,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { } type res struct { want *domain.ObjectDetails - err func(error) bool + err error } tests := []struct { name string @@ -5200,9 +5207,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-FRHBH", "Errors.IDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-FRHBH", "Errors.IDMissing"), }, }, { @@ -5217,9 +5222,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { provider: AppleProvider{}, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-SFm4l", "Errors.IDP.ClientIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-SFm4l", "Errors.IDP.ClientIDMissing"), }, }, { @@ -5236,9 +5239,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-SG34t", "Errors.IDP.TeamIDMissing")) - }, + err: zerrors.ThrowInvalidArgument(nil, "ORG-SG34t", "Errors.IDP.TeamIDMissing"), }, }, { @@ -5256,10 +5257,27 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { }, }, res{ - err: func(err error) bool { - return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-Gh4z2", "Errors.IDP.KeyIDMissing")) + err: zerrors.ThrowInvalidArgument(nil, "ORG-Gh4z2", "Errors.IDP.KeyIDMissing"), + }, + }, + { + "invalid key", + fields{ + eventstore: expectEventstore(), + }, + args{ + ctx: context.Background(), + id: "id1", + provider: AppleProvider{ + ClientID: "clientID", + TeamID: "teamID", + KeyID: "keyID", + PrivateKey: []byte("invalid"), }, }, + res{ + err: zerrors.ThrowInvalidArgument(nil, "ORG-eWSDf", "Errors.IDP.InvalidPrivateKey"), + }, }, { name: "not found", @@ -5279,7 +5297,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { }, }, res: res{ - err: zerrors.IsNotFound, + err: zerrors.ThrowNotFound(nil, "ORG-SG3bh", "Errors.IDPConfig.NotExisting"), }, }, { @@ -5298,7 +5316,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("privateKey"), + Crypted: privateKeyPKCS8, }, nil, idp.Options{}, @@ -5355,7 +5373,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", - Crypted: []byte("newPrivateKey"), + Crypted: privateKeyPKCS8, }), idp.ChangeAppleScopes([]string{"name", "email"}), idp.ChangeAppleOptions(idp.OptionChanges{ @@ -5380,7 +5398,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { ClientID: "clientID2", TeamID: "teamID2", KeyID: "keyID2", - PrivateKey: []byte("newPrivateKey"), + PrivateKey: privateKeyPKCS8, Scopes: []string{"name", "email"}, IDPOptions: idp.Options{ IsCreationAllowed: true, @@ -5402,12 +5420,7 @@ func TestCommandSide_UpdateOrgAppleIDP(t *testing.T) { idpConfigEncryption: tt.fields.secretCrypto, } got, err := c.UpdateOrgAppleProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.id, tt.args.provider) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } + assert.ErrorIs(t, err, tt.res.err) if tt.res.err == nil { assertObjectDetails(t, tt.res.want, got) } diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index 0aadba35c..cd84a5f15 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "encoding/base64" "encoding/json" + "slices" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -41,7 +42,7 @@ func (c *CryptoValue) Value() (driver.Value, error) { return json.Marshal(c) } -func (c *CryptoValue) Scan(src interface{}) error { +func (c *CryptoValue) Scan(src any) error { if b, ok := src.([]byte); ok { return json.Unmarshal(b, c) } @@ -58,6 +59,9 @@ func Crypt(value []byte, alg EncryptionAlgorithm) (*CryptoValue, error) { } func Encrypt(value []byte, alg EncryptionAlgorithm) (*CryptoValue, error) { + if alg == nil { + return nil, zerrors.ThrowInvalidArgument(nil, "CRYPT-03ltQd", "input encryption algorithm cannot be nil") + } encrypted, err := alg.Encrypt(value) if err != nil { return nil, zerrors.ThrowInternal(err, "CRYPT-qCD0JB", "error encrypting value") @@ -106,13 +110,17 @@ func DecryptString(value *CryptoValue, alg EncryptionAlgorithm) (string, error) } func checkEncryptionAlgorithm(value *CryptoValue, alg EncryptionAlgorithm) error { + if value == nil { + return zerrors.ThrowInvalidArgument(nil, "CRYPT-mNsQwe", "input value cannot be nil") + } + if alg == nil { + return zerrors.ThrowInvalidArgument(nil, "CRYPT-paiRey", "input encryption algorithm cannot be nil") + } if value.Algorithm != alg.Algorithm() { return zerrors.ThrowInvalidArgument(nil, "CRYPT-Nx7XlT", "value was encrypted with a different key") } - for _, id := range alg.DecryptionKeyIDs() { - if id == value.KeyID { - return nil - } + if slices.Contains(alg.DecryptionKeyIDs(), value.KeyID) { + return nil } return zerrors.ThrowInvalidArgument(nil, "CRYPT-Kq12vn", "value was encrypted with a different key") } @@ -121,6 +129,9 @@ func CheckToken(alg EncryptionAlgorithm, token string, content string) error { if token == "" { return zerrors.ThrowPermissionDenied(nil, "CRYPTO-Sfefs", "Errors.Intent.InvalidToken") } + if alg == nil { + return zerrors.ThrowInvalidArgument(nil, "CRYPT-edCJsp", "input encryption algorithm cannot be nil") + } data, err := base64.RawURLEncoding.DecodeString(token) if err != nil { return zerrors.ThrowPermissionDenied(err, "CRYPTO-Swg31", "Errors.Intent.InvalidToken") diff --git a/internal/crypto/crypto_test.go b/internal/crypto/crypto_test.go index c5b4d17dd..4e080d90c 100644 --- a/internal/crypto/crypto_test.go +++ b/internal/crypto/crypto_test.go @@ -69,10 +69,15 @@ func TestCrypt(t *testing.T) { wantErr bool }{ { - "encrypt", - args{[]byte("test"), &mockEncCrypto{}}, - &CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, - false, + name: "encrypt ok", + args: args{[]byte("test"), &mockEncCrypto{}}, + want: &CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, + wantErr: false, + }, + { + name: "encryption algorithm nil should return error", + args: args{[]byte("test"), nil}, + wantErr: true, }, } for _, tt := range tests { @@ -101,10 +106,15 @@ func TestEncrypt(t *testing.T) { wantErr bool }{ { - "ok", - args{[]byte("test"), &mockEncCrypto{}}, - &CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, - false, + name: "ok", + args: args{[]byte("test"), &mockEncCrypto{}}, + want: &CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, + wantErr: false, + }, + { + name: "encryption algorithm nil should return error", + args: args{[]byte("test"), nil}, + wantErr: true, }, } for _, tt := range tests { @@ -133,16 +143,25 @@ func TestDecrypt(t *testing.T) { wantErr bool }{ { - "ok", - args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, &mockEncCrypto{}}, - []byte("test"), - false, + name: "ok", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, &mockEncCrypto{}}, + want: []byte("test"), + wantErr: false, }, { - "wrong id", - args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, &mockEncCrypto{}}, - nil, - true, + name: "wrong id", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, &mockEncCrypto{}}, + wantErr: true, + }, + { + name: "when encryption algorithm is nil should return error", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, nil}, + wantErr: true, + }, + { + name: "when crypto value is nil should return error", + args: args{nil, &mockEncCrypto{}}, + wantErr: true, }, } for _, tt := range tests { @@ -171,16 +190,25 @@ func TestDecryptString(t *testing.T) { wantErr bool }{ { - "ok", - args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, &mockEncCrypto{}}, - "test", - false, + name: "ok", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")}, &mockEncCrypto{}}, + want: "test", + wantErr: false, }, { - "wrong id", - args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, &mockEncCrypto{}}, - "", - true, + name: "wrong id", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, &mockEncCrypto{}}, + wantErr: true, + }, + { + name: "when encryption algorithm is nil should return error", + args: args{&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID2", Crypted: []byte("test")}, nil}, + wantErr: true, + }, + { + name: "when crypto value is nil should return error", + args: args{nil, &mockEncCrypto{}}, + wantErr: true, }, } for _, tt := range tests { diff --git a/internal/crypto/rsa.go b/internal/crypto/rsa.go index 3fd9a7756..bba27b70a 100644 --- a/internal/crypto/rsa.go +++ b/internal/crypto/rsa.go @@ -2,6 +2,7 @@ package crypto import ( "bytes" + "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -11,6 +12,8 @@ import ( "fmt" "math/big" "time" + + "github.com/zitadel/zitadel/internal/zerrors" ) func GenerateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) { @@ -128,18 +131,40 @@ func PrivateKeyToBytes(priv *rsa.PrivateKey) []byte { ) } +func PrivateKeyToBytesPKCS8(priv crypto.PrivateKey) ([]byte, error) { + der, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return nil, err + } + buffer := bytes.NewBuffer(nil) + err = pem.Encode(buffer, + &pem.Block{ + Type: "PRIVATE KEY", + Bytes: der, + }, + ) + if err != nil { + return nil, err + } + return buffer.Bytes(), nil +} + func PublicKeyToBytes(pub *rsa.PublicKey) ([]byte, error) { pubASN1, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return nil, err } - pubBytes := pem.EncodeToMemory(&pem.Block{ + buffer := bytes.NewBuffer(nil) + err = pem.Encode(buffer, &pem.Block{ Type: "RSA PUBLIC KEY", Bytes: pubASN1, }) + if err != nil { + return nil, err + } - return pubBytes, nil + return buffer.Bytes(), nil } func BytesToPrivateKey(priv []byte) (*rsa.PrivateKey, error) { @@ -160,6 +185,25 @@ func BytesToPrivateKey(priv []byte) (*rsa.PrivateKey, error) { return key, nil } +func BytesToPrivateKeyPKCS8[T crypto.PrivateKey](priv []byte) (T, error) { + var zero T + if len(priv) == 0 { + return zero, ErrEmpty + } + block, _ := pem.Decode(priv) + if block == nil { + return zero, ErrEmpty + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return zero, err + } + if k, ok := key.(T); ok { + return k, nil + } + return zero, zerrors.ThrowInvalidArgumentf(nil, "CRYP-9n2s3", "wrong type: expected %T, got %T", zero, key) +} + var ErrEmpty = errors.New("cannot decode, empty data") func BytesToPublicKey(pub []byte) (*rsa.PublicKey, error) { diff --git a/internal/idp/providers/apple/apple.go b/internal/idp/providers/apple/apple.go index 57023410d..3a702b94b 100644 --- a/internal/idp/providers/apple/apple.go +++ b/internal/idp/providers/apple/apple.go @@ -1,14 +1,14 @@ package apple import ( - "crypto/x509" - "encoding/pem" + "crypto/ecdsa" "time" "github.com/go-jose/go-jose/v4" "github.com/zitadel/oidc/v3/pkg/crypto" openid "github.com/zitadel/oidc/v3/pkg/oidc" + zcrypto "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp/providers/oidc" ) @@ -42,9 +42,7 @@ func New(clientID, teamID, keyID, callbackURL string, key []byte, scopes []strin // clientSecretFromPrivateKey uses the private key to create and sign a JWT, which has to be used as client_secret at Apple. func clientSecretFromPrivateKey(key []byte, teamID, clientID, keyID string) (string, error) { - block, _ := pem.Decode(key) - b := block.Bytes - pk, err := x509.ParsePKCS8PrivateKey(b) + pk, err := BytesToPrivateKey(key) if err != nil { return "", err } @@ -66,3 +64,7 @@ func clientSecretFromPrivateKey(key []byte, teamID, clientID, keyID string) (str IssuedAt: openid.FromTime(iat), }, signer) } + +func BytesToPrivateKey(key []byte) (*ecdsa.PrivateKey, error) { + return zcrypto.BytesToPrivateKeyPKCS8[*ecdsa.PrivateKey](key) +} diff --git a/internal/static/i18n/ar.yaml b/internal/static/i18n/ar.yaml index 68e566d51..051c92db2 100644 --- a/internal/static/i18n/ar.yaml +++ b/internal/static/i18n/ar.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "النطاق موجود بالفعل" InvalidCharacter: "يسمح فقط بالأحرف الأبجدية الرقمية و . و - للنطاق" EmptyString: "تم استبدال الأحرف غير الرقمية والأبجدية غير الصالحة بمساحات فارغة والنطاق الناتج هو سلسلة فارغة" - IDP: - InvalidSearchQuery: "استعلام بحث غير صالح" - ClientIDMissing: "معرف العميل مفقود" - TeamIDMissing: "معرف الفريق مفقود" - KeyIDMissing: "معرف المفتاح مفقود" - PrivateKeyMissing: "المفتاح الخاص مفقود" LoginPolicy: NotFound: "سياسة تسجيل الدخول غير موجودة" Invalid: "سياسة تسجيل الدخول غير صالحة" @@ -609,6 +603,13 @@ Errors: Duplicate: "معرف مفتاح الويب ليس فريداً" NoActive: "لم يتم العثور على مفتاح ويب نشط" NotFound: "مفتاح الويب غير موجود" + IDP: + InvalidSearchQuery: "استعلام بحث غير صالح" + ClientIDMissing: "معرف العميل مفقود" + TeamIDMissing: "معرف الفريق مفقود" + KeyIDMissing: "معرف المفتاح مفقود" + PrivateKeyMissing: "المفتاح الخاص مفقود" + InvalidPrivateKey: "تنسيق مفتاح خاص غير صالح" AggregateTypes: action: "إجراء" diff --git a/internal/static/i18n/bg.yaml b/internal/static/i18n/bg.yaml index f71cd3a35..934adbbdc 100644 --- a/internal/static/i18n/bg.yaml +++ b/internal/static/i18n/bg.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Домейнът вече съществува" InvalidCharacter: "Само буквено-цифрови знаци, . " EmptyString: "Невалидни нецифрови и азбучни знаци бяха заменени с празни интервали и полученият домейн е празен низ" - IDP: - InvalidSearchQuery: "Невалидна заявка за търсене" - ClientIDMissing: "Липсва ClientID" - TeamIDMissing: "TeamID липсва" - KeyIDMissing: "Липсва KeyID" - PrivateKeyMissing: "Липсва частен ключ" LoginPolicy: NotFound: "Правилата за влизане не са намерени" Invalid: "Правилата за влизане са невалидни" @@ -613,6 +607,13 @@ Errors: Duplicate: "ID на уеб ключ не е уникален" NoActive: "Не е намерен активен уеб ключ" NotFound: "Уеб ключът не е намерен" + IDP: + InvalidSearchQuery: "Невалидна заявка за търсене" + ClientIDMissing: "Липсва ClientID" + TeamIDMissing: "TeamID липсва" + KeyIDMissing: "Липсва KeyID" + PrivateKeyMissing: "Липсва частен ключ" + InvalidPrivateKey: "Невалиден формат на частния ключ" AggregateTypes: action: "Действие" diff --git a/internal/static/i18n/cs.yaml b/internal/static/i18n/cs.yaml index 9eb51cf0f..73f9badf1 100644 --- a/internal/static/i18n/cs.yaml +++ b/internal/static/i18n/cs.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Doména již existuje" InvalidCharacter: "Pro doménu jsou povoleny pouze alfanumerické znaky, . a -" EmptyString: "Neplatné nečíselné a nealfabetické znaky byly nahrazeny prázdnými místy a výsledná doména je prázdný řetězec" - IDP: - InvalidSearchQuery: "Neplatný vyhledávací dotaz" - ClientIDMissing: "Chybí ClientID" - TeamIDMissing: "Chybí TeamID" - KeyIDMissing: "Chybí KeyID" - PrivateKeyMissing: "Chybí privátní klíč" LoginPolicy: NotFound: "Přihlašovací politika nenalezena" Invalid: "Přihlašovací politika je neplatná" @@ -611,6 +605,13 @@ Errors: Duplicate: "ID webového klíče není jedinečné" NoActive: "Nebyl nalezen žádný aktivní webový klíč" NotFound: "Webový klíč nebyl nalezen" + IDP: + InvalidSearchQuery: "Neplatný vyhledávací dotaz" + ClientIDMissing: "Chybí ClientID" + TeamIDMissing: "Chybí TeamID" + KeyIDMissing: "Chybí KeyID" + PrivateKeyMissing: "Chybí privátní klíč" + InvalidPrivateKey: "Neplatný formát privátního klíče" AggregateTypes: action: "Akce" diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index c4d5cc1da..0ebb03ba8 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Domäne existiert bereits" InvalidCharacter: "Nur alphanumerische Zeichen, . und - sind für eine Domäne erlaubt" EmptyString: "Ungültige nicht numerische und alphabetische Zeichen wurden durch Leerzeichen ersetzt und die resultierende Domäne ist eine leere Zeichenfolge" - IDP: - InvalidSearchQuery: "Ungültiger Suchparameter" - ClientIDMissing: "ClientID fehlt" - TeamIDMissing: "TeamID fehlt" - KeyIDMissing: "KeyID fehlt" - PrivateKeyMissing: "Private Key fehlt" LoginPolicy: NotFound: "Login Policy konnte nicht gefunden werden" Invalid: "Login Policy ist ungültig" @@ -613,6 +607,13 @@ Errors: Duplicate: "Webschlüssel-ID nicht eindeutig" NoActive: "Kein aktiver Webschlüssel gefunden" NotFound: "Webschlüssel nicht gefunden" + IDP: + InvalidSearchQuery: "Ungültiger Suchparameter" + ClientIDMissing: "ClientID fehlt" + TeamIDMissing: "TeamID fehlt" + KeyIDMissing: "KeyID fehlt" + PrivateKeyMissing: "Private Key fehlt" + InvalidPrivateKey: "Ungültiges Format des privaten Schlüssels" AggregateTypes: action: "Action" diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index c1e5490c0..fd4986a4d 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -238,12 +238,6 @@ Errors: AlreadyExists: "Domain already exists" InvalidCharacter: "Only alphanumeric characters, . and - are allowed for a domain" EmptyString: "Invalid non numeric and alphabetical characters were replaced with empty spaces and resulting domain is an empty string" - IDP: - InvalidSearchQuery: "Invalid search query" - ClientIDMissing: "ClientID missing" - TeamIDMissing: "TeamID missing" - KeyIDMissing: "KeyID missing" - PrivateKeyMissing: "Private Key missing" LoginPolicy: NotFound: "Login Policy not found" Invalid: "Login Policy is invalid" @@ -615,6 +609,13 @@ Errors: Duplicate: "Web key ID not unique" NoActive: "No active web key found" NotFound: "Web key not found" + IDP: + InvalidSearchQuery: "Invalid search query" + ClientIDMissing: "ClientID missing" + TeamIDMissing: "TeamID missing" + KeyIDMissing: "KeyID missing" + PrivateKeyMissing: "Private Key missing" + InvalidPrivateKey: "Invalid Private Key format" AggregateTypes: action: "Action" diff --git a/internal/static/i18n/es.yaml b/internal/static/i18n/es.yaml index 4c0a4d412..a651d3795 100644 --- a/internal/static/i18n/es.yaml +++ b/internal/static/i18n/es.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "El dominio ya existe" InvalidCharacter: "Solo caracteres alfanuméricos, . y - se permiten para un dominio" EmptyString: "Los caracteres alfabéticos y no numéricos no válidos se reemplazaron con espacios vacíos y el dominio resultante es una cadena vacía" - IDP: - InvalidSearchQuery: "Consulta de búsqueda no válida" - ClientIDMissing: "Falta ClientID" - TeamIDMissing: "Falta TeamID" - KeyIDMissing: "Falta KeyID" - PrivateKeyMissing: "Falta la clave privada" LoginPolicy: NotFound: "Política de inicio de sesión no encontrada" Invalid: "Política de inicio de sesión no es válida" @@ -613,6 +607,13 @@ Errors: Duplicate: "ID de clave web no único" NoActive: "No se encontró ninguna clave web activa" NotFound: "Clave web no encontrada" + IDP: + InvalidSearchQuery: "Consulta de búsqueda no válida" + ClientIDMissing: "Falta ClientID" + TeamIDMissing: "Falta TeamID" + KeyIDMissing: "Falta KeyID" + PrivateKeyMissing: "Falta la clave privada" + InvalidPrivateKey: "Formato de clave privada inválido" AggregateTypes: action: "Acción" diff --git a/internal/static/i18n/fr.yaml b/internal/static/i18n/fr.yaml index c7624b295..05408e237 100644 --- a/internal/static/i18n/fr.yaml +++ b/internal/static/i18n/fr.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Le domaine existe déjà" InvalidCharacter: "Seuls les caractères alphanumériques, . et - sont autorisés pour un domaine" EmptyString: "Les caractères non numériques et alphabétiques non valides ont été remplacés par des espaces vides et le domaine résultant est une chaîne vide" - IDP: - InvalidSearchQuery: "Paramètre de recherche non valide" - ClientIDMissing: "ID client manquant" - TeamIDMissing: "TeamID manquant" - KeyIDMissing: "ID de clé manquant" - PrivateKeyMissing: "clé privée manquante" LoginPolicy: NotFound: "Politique de connexion non trouvée" Invalid: "La politique de connexion n'est pas valide" @@ -613,6 +607,13 @@ Errors: Duplicate: "L'ID de clé Web n'est pas unique" NoActive: "Aucune clé Web active trouvée" NotFound: "Clé Web introuvable" + IDP: + InvalidSearchQuery: "Paramètre de recherche non valide" + ClientIDMissing: "ID client manquant" + TeamIDMissing: "TeamID manquant" + KeyIDMissing: "ID de clé manquant" + PrivateKeyMissing: "clé privée manquante" + InvalidPrivateKey: "Format de clé privée invalide" AggregateTypes: action: "Action" diff --git a/internal/static/i18n/hu.yaml b/internal/static/i18n/hu.yaml index 66a64ec8d..816e4f977 100644 --- a/internal/static/i18n/hu.yaml +++ b/internal/static/i18n/hu.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "A domain már létezik" InvalidCharacter: "Csak alfanumerikus karakterek, . és - engedélyezettek a domain számára" EmptyString: "Érvénytelen nem numerikus és alfabetikus karaktereket üres helyekre cseréltük és az eredményül kapott domain egy üres string" - IDP: - InvalidSearchQuery: "Érvénytelen keresési lekérdezés" - ClientIDMissing: "ClientID hiányzik" - TeamIDMissing: "TeamID hiányzik" - KeyIDMissing: "KeyID hiányzik" - PrivateKeyMissing: "Privát kulcs hiányzik" LoginPolicy: NotFound: "Bejelentkezési politika nem található" Invalid: "Bejelentkezési politika érvénytelen" @@ -613,6 +607,13 @@ Errors: Duplicate: "A webkulcs azonosító nem egyedi" NoActive: "Aktív web kulcs nem található" NotFound: "Web kulcs nem található" + IDP: + InvalidSearchQuery: "Érvénytelen keresési lekérdezés" + ClientIDMissing: "ClientID hiányzik" + TeamIDMissing: "TeamID hiányzik" + KeyIDMissing: "KeyID hiányzik" + PrivateKeyMissing: "Privát kulcs hiányzik" + InvalidPrivateKey: "Érvénytelen titkos kulcs formátum" AggregateTypes: action: "Művelet" diff --git a/internal/static/i18n/id.yaml b/internal/static/i18n/id.yaml index e591b2064..e7bba9e47 100644 --- a/internal/static/i18n/id.yaml +++ b/internal/static/i18n/id.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Domain sudah ada" InvalidCharacter: "Hanya karakter alfanumerik, . " EmptyString: "Karakter non numerik dan alfabet yang tidak valid diganti dengan spasi kosong dan domain yang dihasilkan berupa string kosong" - IDP: - InvalidSearchQuery: "Kueri penelusuran tidak valid" - ClientIDMissing: "ID Klien tidak ada" - TeamIDMissing: "ID Tim hilang" - KeyIDMissing: "ID Kunci hilang" - PrivateKeyMissing: "Kunci Pribadi hilang" LoginPolicy: NotFound: "Kebijakan Login tidak ditemukan" Invalid: "Kebijakan Masuk tidak valid" @@ -608,6 +602,13 @@ Errors: Duplicate: "ID kunci web tidak unik" NoActive: "Tidak ditemukan kunci web aktif" NotFound: "Kunci web tidak ditemukan" + IDP: + InvalidSearchQuery: "Kueri penelusuran tidak valid" + ClientIDMissing: "ID Klien tidak ada" + TeamIDMissing: "ID Tim hilang" + KeyIDMissing: "ID Kunci hilang" + PrivateKeyMissing: "Kunci Pribadi hilang" + InvalidPrivateKey: "Format kunci pribadi tidak valid" AggregateTypes: action: "Tindakan" diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index 534b6dda2..dc9affb95 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Il dominio già esistente" InvalidCharacter: "Solo caratteri alfanumerici, . e - sono consentiti per un dominio" EmptyString: "I caratteri non numerici e alfabetici non validi sono stati sostituiti con spazi vuoti e il dominio risultante è una stringa vuota" - IDP: - InvalidSearchQuery: "Parametro di ricerca non valido" - ClientIDMissing: "ClientID mancante" - TeamIDMissing: "TeamID mancante" - KeyIDMissing: "ID chiave mancante" - PrivateKeyMissing: "Chiave privata mancante" LoginPolicy: NotFound: "Impostazioni di accesso non trovati" Invalid: "Impostazioni di accesso non sono validi" @@ -613,6 +607,13 @@ Errors: Duplicate: "ID chiave Web non univoco" NoActive: "Nessuna chiave Web attiva trovata" NotFound: "Chiave Web non trovata" + IDP: + InvalidSearchQuery: "Parametro di ricerca non valido" + ClientIDMissing: "ClientID mancante" + TeamIDMissing: "TeamID mancante" + KeyIDMissing: "ID chiave mancante" + PrivateKeyMissing: "Chiave privata mancante" + InvalidPrivateKey: "Formato chiave privata non valido" AggregateTypes: action: "Azione" diff --git a/internal/static/i18n/ja.yaml b/internal/static/i18n/ja.yaml index 8581b88b0..d0cd04d59 100644 --- a/internal/static/i18n/ja.yaml +++ b/internal/static/i18n/ja.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "ドメインはすでに存在します" InvalidCharacter: "ドメインは英数字、'.'、'-'のみ使用可能です。" EmptyString: "無効な数字およびアルファベット以外の文字は空のスペースに置き換えられ、結果のドメインは空の文字列になります" - IDP: - InvalidSearchQuery: "無効な検索クエリです" - ClientIDMissing: "クライアントIDがありません" - TeamIDMissing: "チームIDがありません" - KeyIDMissing: "キーIDがありません" - PrivateKeyMissing: "秘密キーがありません" LoginPolicy: NotFound: "ログインポリシーが見つかりません" Invalid: "無効なログインポリシーです" @@ -614,6 +608,13 @@ Errors: Duplicate: "Web キー ID が一意ではありません" NoActive: "アクティブな Web キーが見つかりません" NotFound: "Web キーが見つかりません" + IDP: + InvalidSearchQuery: "無効な検索クエリです" + ClientIDMissing: "クライアントIDがありません" + TeamIDMissing: "チームIDがありません" + KeyIDMissing: "キーIDがありません" + PrivateKeyMissing: "秘密キーがありません" + InvalidPrivateKey: "無効な秘密鍵形式" AggregateTypes: action: "アクション" diff --git a/internal/static/i18n/ko.yaml b/internal/static/i18n/ko.yaml index a7cbcf954..b7f1c351b 100644 --- a/internal/static/i18n/ko.yaml +++ b/internal/static/i18n/ko.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "도메인이 이미 존재합니다" InvalidCharacter: "도메인에는 영숫자, ., -만 허용됩니다" EmptyString: "유효하지 않은 문자들이 비어 있는 문자열로 대체되었고 결과 도메인이 비어 있습니다" - IDP: - InvalidSearchQuery: "잘못된 검색 쿼리입니다" - ClientIDMissing: "ClientID가 누락되었습니다" - TeamIDMissing: "TeamID가 누락되었습니다" - KeyIDMissing: "KeyID가 누락되었습니다" - PrivateKeyMissing: "개인 키가 누락되었습니다" LoginPolicy: NotFound: "로그인 정책을 찾을 수 없습니다" Invalid: "로그인 정책이 유효하지 않습니다" @@ -614,6 +608,13 @@ Errors: Duplicate: "웹 키 ID가 고유하지 않습니다" NoActive: "활성 웹 키가 없습니다" NotFound: "웹 키를 찾을 수 없습니다" + IDP: + InvalidSearchQuery: "잘못된 검색 쿼리입니다" + ClientIDMissing: "ClientID가 누락되었습니다" + TeamIDMissing: "TeamID가 누락되었습니다" + KeyIDMissing: "KeyID가 누락되었습니다" + PrivateKeyMissing: "개인 키가 누락되었습니다" + InvalidPrivateKey: "유효하지 않은 개인 키 형식" AggregateTypes: action: "작업" diff --git a/internal/static/i18n/mk.yaml b/internal/static/i18n/mk.yaml index 37ae0d2c1..d12e8d73d 100644 --- a/internal/static/i18n/mk.yaml +++ b/internal/static/i18n/mk.yaml @@ -235,12 +235,6 @@ Errors: AlreadyExists: "Доменот веќе постои" InvalidCharacter: "Дозволени се само алфанумерички знаци, . и - се дозволени за домен" EmptyString: "Неважечките ненумерички и азбучни знаци се заменети со празни места и добиениот домен е празна низа" - IDP: - InvalidSearchQuery: "Невалидно пребарување" - ClientID Missing: "ClientID недостасува" - TeamIDMissing: "TeamID недостасува" - Клучен ID Недостасува: "Недостасува ID на клуч" - PrivateKeyMissing: "Недостасува приватен клуч" LoginPolicy: NotFound: "Политиката за најавување не е пронајдена" Invalid: "Политиката за најавување е невалидна" @@ -612,6 +606,13 @@ Errors: Duplicate: "ID на веб-клучот не е единствен" NoActive: "Не е пронајден активен веб-клуч" NotFound: "Веб-клучот не е пронајден" + IDP: + InvalidSearchQuery: "Невалидно пребарување" + ClientIDMissing: "ClientID недостасува" + TeamIDMissing: "TeamID недостасува" + KeyIDMissing: "Недостасува ID на клуч" + PrivateKeyMissing: "Недостасува приватен клуч" + InvalidPrivateKey: "Невалиден формат на приватен клуч" AggregateTypes: action: "Акција" diff --git a/internal/static/i18n/nl.yaml b/internal/static/i18n/nl.yaml index ba1a0d6fd..93ab9a63d 100644 --- a/internal/static/i18n/nl.yaml +++ b/internal/static/i18n/nl.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Domein bestaat al" InvalidCharacter: "Alleen alfanumerieke tekens, . en - zijn toegestaan voor een domein" EmptyString: "Ongeldige niet-numerieke en alfabetische tekens zijn vervangen door lege spaties en het resulterende domein is een lege string" - IDP: - InvalidSearchQuery: "Ongeldige zoekopdracht" - ClientIDMissing: "ClientID ontbreekt" - TeamIDMissing: "TeamID ontbreekt" - KeyIDMissing: "KeyID ontbreekt" - PrivateKeyMissing: "Privésleutel ontbreekt" LoginPolicy: NotFound: "Login Beleid niet gevonden" Invalid: "Login Beleid is ongeldig" @@ -613,6 +607,13 @@ Errors: Duplicate: "Websleutel-ID niet uniek" NoActive: "Geen actieve websleutel gevonden" NotFound: "Websleutel niet gevonden" + IDP: + InvalidSearchQuery: "Ongeldige zoekopdracht" + ClientIDMissing: "ClientID ontbreekt" + TeamIDMissing: "TeamID ontbreekt" + KeyIDMissing: "KeyID ontbreekt" + PrivateKeyMissing: "Privésleutel ontbreekt" + InvalidPrivateKey: "Ongeldig formaat van privésleutel" AggregateTypes: action: "Actie" diff --git a/internal/static/i18n/pl.yaml b/internal/static/i18n/pl.yaml index f941dbbcf..8472ec91c 100644 --- a/internal/static/i18n/pl.yaml +++ b/internal/static/i18n/pl.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Domena już istnieje" InvalidCharacter: "Tylko znaki alfanumeryczne, . i - są dozwolone dla domeny" EmptyString: "Nieprawidłowe znaki inne niż numeryczne i alfabetyczne zostały zastąpione pustymi spacjami, a wynikowa domena jest pustym ciągiem znaków" - IDP: - InvalidSearchQuery: "Nieprawidłowe zapytanie wyszukiwania" - ClientIDMissing: "Brak ClientID" - TeamIDMissing: "Brak TeamID" - KeyIDMissing: "Brak KeyID" - PrivateKeyMissing: "Brak klucza prywatnego" LoginPolicy: NotFound: "Polityka logowania nie znaleziona" Invalid: "Polityka logowania jest nieprawidłowa" @@ -613,6 +607,13 @@ Errors: Duplicate: "Identyfikator klucza internetowego nie jest unikalny" NoActive: "Nie znaleziono aktywnego klucza internetowego" NotFound: "Nie znaleziono klucza internetowego" + IDP: + InvalidSearchQuery: "Nieprawidłowe zapytanie wyszukiwania" + ClientIDMissing: "Brak ClientID" + TeamIDMissing: "Brak TeamID" + KeyIDMissing: "Brak KeyID" + PrivateKeyMissing: "Brak klucza prywatnego" + InvalidPrivateKey: "Nieprawidłowy format klucza prywatnego" AggregateTypes: action: "Działanie" diff --git a/internal/static/i18n/pt.yaml b/internal/static/i18n/pt.yaml index 8c5bd3862..82edc83e6 100644 --- a/internal/static/i18n/pt.yaml +++ b/internal/static/i18n/pt.yaml @@ -235,12 +235,6 @@ Errors: AlreadyExists: "Domínio já existe" InvalidCharacter: "Apenas caracteres alfanuméricos, . e - são permitidos para um domínio" EmptyString: "Caracteres não numéricos e alfabéticos inválidos foram substituídos por espaços vazios e o domínio resultante é uma string vazia" - IDP: - InvalidSearchQuery: "Consulta de pesquisa inválida" - ClientIDMissing: "ClientID ausente" - TeamIDMissing: "TeamID ausente" - KeyIDMissing: "KeyID ausente" - PrivateKeyMissing: "Chave privada ausente" LoginPolicy: NotFound: "Política de login não encontrada" Invalid: "Política de login é inválida" @@ -612,6 +606,13 @@ Errors: Duplicate: "ID da chave Web não exclusivo" NoActive: "Nenhuma chave web ativa encontrada" NotFound: "Chave Web não encontrada" + IDP: + InvalidSearchQuery: "Consulta de pesquisa inválida" + ClientIDMissing: "ClientID ausente" + TeamIDMissing: "TeamID ausente" + KeyIDMissing: "KeyID ausente" + PrivateKeyMissing: "Chave privada ausente" + InvalidPrivateKey: "Formato de chave privada inválido" AggregateTypes: action: "Ação" diff --git a/internal/static/i18n/ro.yaml b/internal/static/i18n/ro.yaml index 309a6c476..1f939f050 100644 --- a/internal/static/i18n/ro.yaml +++ b/internal/static/i18n/ro.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "Domeniul există deja" InvalidCharacter: "Numai caractere alfanumerice, . și - sunt permise pentru un domeniu" EmptyString: "Caracterele non-numerice și alfabetice invalide au fost înlocuite cu spații goale și domeniul rezultat este un șir gol" - IDP: - InvalidSearchQuery: "Interogare de căutare invalidă" - ClientIDMissing: "ClientID lipsă" - TeamIDMissing: "TeamID lipsă" - KeyIDMissing: "KeyID lipsă" - PrivateKeyMissing: "Cheie Privată lipsă" LoginPolicy: NotFound: "Politica de conectare nu a fost găsită" Invalid: "Politica de conectare este invalidă" @@ -609,6 +603,13 @@ Errors: Duplicate: "ID-ul cheii web nu este unic" NoActive: "Nu a fost găsită nicio cheie web activă" NotFound: "Cheia web nu a fost găsită" + IDP: + InvalidSearchQuery: "Interogare de căutare invalidă" + ClientIDMissing: "ClientID lipsă" + TeamIDMissing: "TeamID lipsă" + KeyIDMissing: "KeyID lipsă" + PrivateKeyMissing: "Cheie Privată lipsă" + InvalidPrivateKey: "Format de cheie privată nevalid" AggregateTypes: action: "Acțiune" diff --git a/internal/static/i18n/ru.yaml b/internal/static/i18n/ru.yaml index 007f46d64..f247d85b6 100644 --- a/internal/static/i18n/ru.yaml +++ b/internal/static/i18n/ru.yaml @@ -230,12 +230,6 @@ Errors: Domain: AlreadyExists: "Домен уже существует" InvalidCharacter: "Только буквенно-цифровые символы, . и - разрешены для домена" - IDP: - InvalidSearchQuery: "Неверный поисковый запрос" - ClientIDMissing: "ClientID отсутствует" - TeamIDMissing: "TeamID отсутствует" - KeyIDMissing: "KeyID отсутствует" - PrivateKeyMissing: "Закрытый ключ отсутствует" LoginPolicy: NotFound: "Политика входа в систему не найдена" Invalid: "Политика входа в систему недействительна" @@ -602,6 +596,13 @@ Errors: Duplicate: "Идентификатор веб-ключа не уникален" NoActive: "Активный веб-ключ не найден" NotFound: "Веб-ключ не найден" + IDP: + InvalidSearchQuery: "Неверный поисковый запрос" + ClientIDMissing: "ClientID отсутствует" + TeamIDMissing: "TeamID отсутствует" + KeyIDMissing: "KeyID отсутствует" + PrivateKeyMissing: "Закрытый ключ отсутствует" + InvalidPrivateKey: "Неверный формат приватного ключа" AggregateTypes: action: "Действие" diff --git a/internal/static/i18n/sv.yaml b/internal/static/i18n/sv.yaml index afed8046c..82d11512a 100644 --- a/internal/static/i18n/sv.yaml +++ b/internal/static/i18n/sv.yaml @@ -236,12 +236,6 @@ Errors: AlreadyExists: "Domänen finns redan" InvalidCharacter: "Endast alfanumeriska tecken, . och - är tillåtna för en domän" EmptyString: "Ogiltiga icke-numeriska och alfabetiska tecken ersattes med tomma utrymmen och den resulterande domänen är en tom sträng" - IDP: - InvalidSearchQuery: "Ogiltig sökfråga" - ClientIDMissing: "ClientID saknas" - TeamIDMissing: "TeamID saknas" - KeyIDMissing: "KeyID saknas" - PrivateKeyMissing: "Privat nyckel saknas" LoginPolicy: NotFound: "Inloggningspolicyn hittades inte" Invalid: "Inloggningspolicyn är ogiltig" @@ -613,6 +607,13 @@ Errors: Duplicate: "Webnyckel-ID är inte unikt" NoActive: "Ingen aktiv webbnyckel hittades" NotFound: "Webnyckel hittades inte" + IDP: + InvalidSearchQuery: "Ogiltig sökfråga" + ClientIDMissing: "ClientID saknas" + TeamIDMissing: "TeamID saknas" + KeyIDMissing: "KeyID saknas" + PrivateKeyMissing: "Privat nyckel saknas" + InvalidPrivateKey: "Ogiltigt format för privat nyckel" AggregateTypes: action: "Åtgärd" diff --git a/internal/static/i18n/tr.yaml b/internal/static/i18n/tr.yaml index 1e1c6c6fd..2683546fc 100644 --- a/internal/static/i18n/tr.yaml +++ b/internal/static/i18n/tr.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "Domain zaten mevcut" InvalidCharacter: "Domain için sadece alfanümerik karakterler, . ve - kullanılabilir" EmptyString: "Geçersiz sayısal olmayan ve alfabetik olmayan karakterler boş alanlarla değiştirildi ve sonuçta domain boş bir dize oldu" - IDP: - InvalidSearchQuery: "Geçersiz arama sorgusu" - ClientIDMissing: "ClientID eksik" - TeamIDMissing: "TeamID eksik" - KeyIDMissing: "KeyID eksik" - PrivateKeyMissing: "Özel Anahtar eksik" LoginPolicy: NotFound: "Giriş Politikası bulunamadı" Invalid: "Giriş Politikası geçersiz" @@ -614,6 +608,13 @@ Errors: Duplicate: "Web anahtarı ID benzersiz değil" NoActive: "Aktif web anahtarı bulunamadı" NotFound: "Web anahtarı bulunamadı" + IDP: + InvalidSearchQuery: "Geçersiz arama sorgusu" + ClientIDMissing: "ClientID eksik" + TeamIDMissing: "TeamID eksik" + KeyIDMissing: "KeyID eksik" + PrivateKeyMissing: "Özel Anahtar eksik" + InvalidPrivateKey: "Geçersiz özel anahtar biçimi" AggregateTypes: action: "Eylem" diff --git a/internal/static/i18n/uk.yaml b/internal/static/i18n/uk.yaml index 2b3de29bb..3911f6e72 100644 --- a/internal/static/i18n/uk.yaml +++ b/internal/static/i18n/uk.yaml @@ -231,12 +231,6 @@ Errors: AlreadyExists: "Домен вже існує" InvalidCharacter: "Для домену дозволені лише буквено-цифрові символи, . та -" EmptyString: "Недійсні не цифрові та не алфавітні символи були замінені порожніми пробілами і результуючий домен є порожнім рядком" - IDP: - InvalidSearchQuery: "Недійсний пошуковий запит" - ClientIDMissing: "Відсутній ClientID" - TeamIDMissing: "Відсутній TeamID" - KeyIDMissing: "Відсутній KeyID" - PrivateKeyMissing: "Відсутній приватний ключ" LoginPolicy: NotFound: "Політика входу не знайдена" Invalid: "Політика входу недійсна" @@ -608,6 +602,13 @@ Errors: Duplicate: "Ідентифікатор веб-ключа не унікальний" NoActive: "Активний веб-ключ не знайдено" NotFound: "Веб-ключ не знайдено" + IDP: + InvalidSearchQuery: "Недійсний пошуковий запит" + ClientIDMissing: "Відсутній ClientID" + TeamIDMissing: "Відсутній TeamID" + KeyIDMissing: "Відсутній KeyID" + PrivateKeyMissing: "Відсутній приватний ключ" + InvalidPrivateKey: "Невірний формат приватного ключа" AggregateTypes: action: "Дія" diff --git a/internal/static/i18n/zh.yaml b/internal/static/i18n/zh.yaml index 3f0d6b9d4..3a2c8dcfd 100644 --- a/internal/static/i18n/zh.yaml +++ b/internal/static/i18n/zh.yaml @@ -237,12 +237,6 @@ Errors: AlreadyExists: "域名已存在" InvalidCharacter: "只有字母数字字符,.和 - 允许用于域名中" EmptyString: "无效的非数字和字母字符被替换为空格,结果域是空字符串" - IDP: - InvalidSearchQuery: "无效的搜索查询" - ClientIDMissing: "客户端 ID 丢失" - TeamIDMissing: "团队 ID 丢失" - KeyIDMissing: "密钥 ID 丢失" - PrivateKeyMissing: "私钥丢失" LoginPolicy: NotFound: "未找到登录策略" Invalid: "登录策略无效" @@ -614,6 +608,13 @@ Errors: Duplicate: "Web 密钥 ID 不唯一" NoActive: "未找到活动 Web 密钥" NotFound: "未找到 Web 密钥" + IDP: + InvalidSearchQuery: "无效的搜索查询" + ClientIDMissing: "客户端 ID 丢失" + TeamIDMissing: "团队 ID 丢失" + KeyIDMissing: "密钥 ID 丢失" + PrivateKeyMissing: "私钥丢失" + InvalidPrivateKey: "无效的私钥格式" AggregateTypes: action: "动作"