fix: prevent possible nil pointer panics (#11728)

# Which Problems Are Solved

We found multiple cases where potentially a panic occurred or could
occur:
- when de/encrypting certain information
- returning apps without any configuration type (?)
- apple IdPs without a proper private key

# How the Problems Are Solved

- Added nil checks
- Check private key format for apple IdPs
- Added necessary helper function

# Additional Changes

Fixed i18n yaml where the IDP errors were indented under `org` instead
of directly under `errors`.

# Additional Context

- requires backport to v4.x

---------

Co-authored-by: Marco A. <[email protected]>
This commit is contained in:
Livio Spring
2026-03-16 14:30:17 +00:00
committed by GitHub
co-authored by Marco A.
parent 7c0e1750f3
commit 500821ce0c
33 changed files with 440 additions and 264 deletions
@@ -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
}
@@ -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,
+4 -1
View File
@@ -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 {
+9
View File
@@ -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 {
+67 -44
View File
@@ -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)
}
+9
View File
@@ -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 {
+58 -45
View File
@@ -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)
}
+16 -5
View File
@@ -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")
+52 -24
View File
@@ -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 {
+46 -2
View File
@@ -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) {
+7 -5
View File
@@ -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)
}
+7 -6
View File
@@ -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: "إجراء"
+7 -6
View File
@@ -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: "Действие"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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: "アクション"
+7 -6
View File
@@ -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: "작업"
+7 -6
View File
@@ -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: "Акција"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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: "Действие"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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"
+7 -6
View File
@@ -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: "Дія"
+7 -6
View File
@@ -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: "动作"