mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-27 22:03:41 +00:00
feat(crypto): use passwap for machine and app secrets (#7657)
* feat(crypto): use passwap for machine and app secrets * fix command package tests * add hash generator command test * naming convention, fix query tests * rename PasswordHasher and cleanup start commands * add reducer tests * fix intergration tests, cleanup old config * add app secret unit tests * solve setup panics * fix push of updated events * add missing event translations * update documentation * solve linter errors * remove nolint:SA1019 as it doesn't seem to help anyway * add nolint to deprecated filter usage * update users migration version * remove unused ClientSecret from APIConfigChangedEvent --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
parent
5931fb8f28
commit
2089992d75
4
Makefile
4
Makefile
@ -115,6 +115,10 @@ core_integration_setup:
|
||||
core_integration_test: core_integration_setup
|
||||
go test -tags=integration -race -p 1 -coverprofile=profile.cov -coverpkg=./internal/...,./cmd/... ./...
|
||||
|
||||
.PHONY: core_integration_test_fast
|
||||
core_integration_test_fast: core_integration_setup
|
||||
go test -tags=integration -p 1 ./...
|
||||
|
||||
.PHONY: console_lint
|
||||
console_lint:
|
||||
cd console && \
|
||||
|
@ -428,7 +428,6 @@ SystemAPIUsers:
|
||||
|
||||
SystemDefaults:
|
||||
SecretGenerators:
|
||||
PasswordSaltCost: 14 # ZITADEL_SYSTEMDEFAULTS_SECRETGENERATORS_PASSWORDSALTCOST
|
||||
MachineKeySize: 2048 # ZITADEL_SYSTEMDEFAULTS_SECRETGENERATORS_MACHINEKEYSIZE
|
||||
ApplicationKeySize: 2048 # ZITADEL_SYSTEMDEFAULTS_SECRETGENERATORS_APPLICATIONKEYSIZE
|
||||
PasswordHasher:
|
||||
@ -482,6 +481,13 @@ SystemDefaults:
|
||||
# - "md5"
|
||||
# - "scrypt"
|
||||
# - "pbkdf2" # verifier for all pbkdf2 hash modes.
|
||||
SecretHasher:
|
||||
# Set hasher configuration for machine users, API and OIDC client secrets.
|
||||
# See PasswordHasher for all possible options
|
||||
Hasher:
|
||||
Algorithm: "bcrypt" # ZITADEL_SYSTEMDEFAULTS_SECRETHASHER_HASHER_ALGORITHM
|
||||
Cost: 4 # ZITADEL_SYSTEMDEFAULTS_SECRETHASHER_HASHER_COST
|
||||
Verifiers:
|
||||
Multifactors:
|
||||
OTP:
|
||||
# If this is empty, the issuer is the requested domain
|
||||
@ -590,7 +596,6 @@ DefaultInstance:
|
||||
# date format: 2023-01-01T00:00:00Z
|
||||
ExpirationDate: # ZITADEL_DEFAULTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE
|
||||
SecretGenerators:
|
||||
PasswordSaltCost: 14 # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_PASSWORDSALTCOST
|
||||
ClientSecret:
|
||||
Length: 64 # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_CLIENTSECRET_LENGTH
|
||||
IncludeLowerLetters: true # ZITADEL_DEFAULTINSTANCE_SECRETGENERATORS_CLIENTSECRET_INCLUDELOWERLETTERS
|
||||
|
@ -23,5 +23,5 @@ func (mig *User11AddLowerFieldsToVerifiedEmail) Execute(ctx context.Context, _ e
|
||||
}
|
||||
|
||||
func (mig *User11AddLowerFieldsToVerifiedEmail) String() string {
|
||||
return "25_user11_add_lower_fields_to_verified_email"
|
||||
return "25_user12_add_lower_fields_to_verified_email"
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
ALTER TABLE IF EXISTS projections.users11_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED;
|
||||
CREATE INDEX IF NOT EXISTS users11_notifications_email_search ON projections.users11_notifications (instance_id, verified_email_lower);
|
||||
ALTER TABLE IF EXISTS projections.users12_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED;
|
||||
CREATE INDEX IF NOT EXISTS users12_notifications_email_search ON projections.users12_notifications (instance_id, verified_email_lower);
|
||||
|
@ -439,7 +439,7 @@ func startAPIs(
|
||||
}
|
||||
apis.RegisterHandlerOnPrefix(openapi.HandlerPrefix, openAPIHandler)
|
||||
|
||||
oidcServer, err := oidc.NewServer(config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, limitingAccessInterceptor, config.Log.Slog())
|
||||
oidcServer, err := oidc.NewServer(ctx, config.OIDC, login.DefaultLoggedOutPath, config.ExternalSecure, commands, queries, authRepo, keys.OIDC, keys.OIDCKey, eventstore, dbClient, userAgentInterceptor, instanceInterceptor.Handler, limitingAccessInterceptor, config.Log.Slog(), config.SystemDefaults.SecretHasher)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to start oidc provider: %w", err)
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ ZITADEL hashes all Passwords and Client Secrets in an non reversible way to furt
|
||||
Passwords and secrets are always hashed with a random salt and stored as an encoded string that contains the Algorithm, its Parameters, Salt and Hash.
|
||||
The storage encoding used by ZITADEL is Modular Crypt Format and a full reference can be found in our [Passwap library](https://github.com/zitadel/passwap#encoding).
|
||||
|
||||
The following hash algorithms are supported for user passwords:
|
||||
The following hash algorithms are supported:
|
||||
|
||||
- argon2i / id[^1]
|
||||
- bcrypt (Default)
|
||||
@ -82,8 +82,6 @@ This allows to increase cost along with growing computing power.
|
||||
ZITADEL allows to import user passwords from systems that use any of the above hashing algorithms.
|
||||
:::
|
||||
|
||||
Client Secrets always use bcrypt.
|
||||
|
||||
### Encrypted Secrets
|
||||
|
||||
Some secrets cannot be hashed because they need to be used in their raw form. These include:
|
||||
|
@ -292,7 +292,7 @@ func getFileFromGCS(ctx context.Context, input *admin_pb.ImportDataRequest_GCSIn
|
||||
return ioutil.ReadAll(reader)
|
||||
}
|
||||
|
||||
func importOrg1(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, ctxData authz.CtxData, org *admin_pb.DataOrg, success *admin_pb.ImportDataSuccess, count *counts, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode, appSecretGenerator crypto.Generator) error {
|
||||
func importOrg1(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, ctxData authz.CtxData, org *admin_pb.DataOrg, success *admin_pb.ImportDataSuccess, count *counts, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode crypto.Generator) error {
|
||||
_, err := s.command.AddOrgWithID(ctx, org.GetOrg().GetName(), ctxData.UserID, ctxData.ResourceOwner, org.GetOrgId(), []string{})
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "org", Id: org.GetOrgId(), Message: err.Error()})
|
||||
@ -325,7 +325,7 @@ func importOrg1(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataEr
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "domain_policy", Id: org.GetOrgId(), Message: err.Error()})
|
||||
}
|
||||
}
|
||||
return importResources(ctx, s, errors, successOrg, org, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode, appSecretGenerator)
|
||||
return importResources(ctx, s, errors, successOrg, org, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode)
|
||||
}
|
||||
|
||||
func importLabelPolicy(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, org *admin_pb.DataOrg) error {
|
||||
@ -584,13 +584,13 @@ func importProjects(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDa
|
||||
return nil
|
||||
}
|
||||
|
||||
func importOIDCApps(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts, appSecretGenerator crypto.Generator) error {
|
||||
func importOIDCApps(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts) error {
|
||||
if org.OidcApps == nil {
|
||||
return nil
|
||||
}
|
||||
for _, app := range org.GetOidcApps() {
|
||||
logging.Debugf("import oidcapplication: %s", app.GetAppId())
|
||||
_, err := s.command.AddOIDCApplicationWithID(ctx, management.AddOIDCAppRequestToDomain(app.App), org.GetOrgId(), app.GetAppId(), appSecretGenerator)
|
||||
_, err := s.command.AddOIDCApplicationWithID(ctx, management.AddOIDCAppRequestToDomain(app.App), org.GetOrgId(), app.GetAppId())
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "oidc_app", Id: app.GetAppId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
@ -605,13 +605,13 @@ func importOIDCApps(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDa
|
||||
return nil
|
||||
}
|
||||
|
||||
func importAPIApps(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts, appSecretGenerator crypto.Generator) error {
|
||||
func importAPIApps(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts) error {
|
||||
if org.ApiApps == nil {
|
||||
return nil
|
||||
}
|
||||
for _, app := range org.GetApiApps() {
|
||||
logging.Debugf("import apiapplication: %s", app.GetAppId())
|
||||
_, err := s.command.AddAPIApplicationWithID(ctx, management.AddAPIAppRequestToDomain(app.GetApp()), org.GetOrgId(), app.GetAppId(), appSecretGenerator)
|
||||
_, err := s.command.AddAPIApplicationWithID(ctx, management.AddAPIAppRequestToDomain(app.GetApp()), org.GetOrgId(), app.GetAppId())
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "api_app", Id: app.GetAppId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
@ -700,7 +700,7 @@ func importProjectRoles(ctx context.Context, s *Server, errors *[]*admin_pb.Impo
|
||||
return nil
|
||||
}
|
||||
|
||||
func importResources(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode, appSecretGenerator crypto.Generator) error {
|
||||
func importResources(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataError, successOrg *admin_pb.ImportDataSuccessOrg, org *admin_pb.DataOrg, count *counts, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode crypto.Generator) error {
|
||||
if err := importOrgDomains(ctx, s, errors, successOrg, org); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -742,10 +742,10 @@ func importResources(ctx context.Context, s *Server, errors *[]*admin_pb.ImportD
|
||||
if err := importProjects(ctx, s, errors, successOrg, org, count); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := importOIDCApps(ctx, s, errors, successOrg, org, count, appSecretGenerator); err != nil {
|
||||
if err := importOIDCApps(ctx, s, errors, successOrg, org, count); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := importAPIApps(ctx, s, errors, successOrg, org, count, appSecretGenerator); err != nil {
|
||||
if err := importAPIApps(ctx, s, errors, successOrg, org, count); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := importAppKeys(ctx, s, errors, successOrg, org, count); err != nil {
|
||||
@ -1023,10 +1023,6 @@ func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*adm
|
||||
success := &admin_pb.ImportDataSuccess{}
|
||||
count := &counts{}
|
||||
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.userCodeAlg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@ -1064,7 +1060,7 @@ func (s *Server) importData(ctx context.Context, orgs []*admin_pb.DataOrg) (*adm
|
||||
count.appKeysCount += len(org.GetAppKeys())
|
||||
}
|
||||
for _, org := range orgs {
|
||||
if err = importOrg1(ctx, s, &errors, ctxData, org, success, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode, appSecretGenerator); err != nil {
|
||||
if err = importOrg1(ctx, s, &errors, ctxData, org, success, count, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessInitCode); err != nil {
|
||||
return &admin_pb.ImportDataResponse{Errors: errors, Success: success}, count, err
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ type Server struct {
|
||||
query *query.Queries
|
||||
assetsAPIDomain func(context.Context) string
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
passwordHashAlg crypto.HashAlgorithm
|
||||
auditLogRetention time.Duration
|
||||
}
|
||||
|
||||
@ -53,7 +52,6 @@ func CreateServer(
|
||||
query: query,
|
||||
assetsAPIDomain: assets.AssetAPI(externalSecure),
|
||||
userCodeAlg: userCodeAlg,
|
||||
passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
|
||||
auditLogRetention: auditLogRetention,
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
change_grpc "github.com/zitadel/zitadel/internal/api/grpc/change"
|
||||
object_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
project_grpc "github.com/zitadel/zitadel/internal/api/grpc/project"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/repository/project"
|
||||
@ -81,11 +80,7 @@ func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChanges
|
||||
}
|
||||
|
||||
func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest) (*mgmt_pb.AddOIDCAppResponse, error) {
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app, err := s.command.AddOIDCApplication(ctx, AddOIDCAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID, appSecretGenerator)
|
||||
app, err := s.command.AddOIDCApplication(ctx, AddOIDCAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -110,11 +105,7 @@ func (s *Server) AddSAMLApp(ctx context.Context, req *mgmt_pb.AddSAMLAppRequest)
|
||||
}
|
||||
|
||||
func (s *Server) AddAPIApp(ctx context.Context, req *mgmt_pb.AddAPIAppRequest) (*mgmt_pb.AddAPIAppResponse, error) {
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app, err := s.command.AddAPIApplication(ctx, AddAPIAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID, appSecretGenerator)
|
||||
app, err := s.command.AddAPIApplication(ctx, AddAPIAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -209,11 +200,7 @@ func (s *Server) RemoveApp(ctx context.Context, req *mgmt_pb.RemoveAppRequest) (
|
||||
}
|
||||
|
||||
func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.RegenerateOIDCClientSecretRequest) (*mgmt_pb.RegenerateOIDCClientSecretResponse, error) {
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := s.command.ChangeOIDCApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID, appSecretGenerator)
|
||||
config, err := s.command.ChangeOIDCApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -228,11 +215,7 @@ func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.Re
|
||||
}
|
||||
|
||||
func (s *Server) RegenerateAPIClientSecret(ctx context.Context, req *mgmt_pb.RegenerateAPIClientSecretRequest) (*mgmt_pb.RegenerateAPIClientSecretResponse, error) {
|
||||
appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := s.command.ChangeAPIApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID, appSecretGenerator)
|
||||
config, err := s.command.ChangeAPIApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -23,13 +23,12 @@ var _ management.ManagementServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
management.UnimplementedManagementServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
assetAPIPrefix func(context.Context) string
|
||||
passwordHashAlg crypto.HashAlgorithm
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
externalSecure bool
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
assetAPIPrefix func(context.Context) string
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
externalSecure bool
|
||||
}
|
||||
|
||||
func CreateServer(
|
||||
@ -40,13 +39,12 @@ func CreateServer(
|
||||
externalSecure bool,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
systemDefaults: sd,
|
||||
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
||||
passwordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
|
||||
userCodeAlg: userCodeAlg,
|
||||
externalSecure: externalSecure,
|
||||
command: command,
|
||||
query: query,
|
||||
systemDefaults: sd,
|
||||
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
||||
userCodeAlg: userCodeAlg,
|
||||
externalSecure: externalSecure,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -800,18 +800,13 @@ func (s *Server) RemoveMachineKey(ctx context.Context, req *mgmt_pb.RemoveMachin
|
||||
}
|
||||
|
||||
func (s *Server) GenerateMachineSecret(ctx context.Context, req *mgmt_pb.GenerateMachineSecretRequest) (*mgmt_pb.GenerateMachineSecretResponse, error) {
|
||||
// use SecretGeneratorTypeAppSecret as the secrets will be used in the client_credentials grant like a client secret
|
||||
secretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.passwordHashAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := s.getUserByID(ctx, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set := new(command.GenerateMachineSecret)
|
||||
details, err := s.command.GenerateMachineSecret(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, secretGenerator, set)
|
||||
details, err := s.command.GenerateMachineSecret(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, set)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func MachineToPb(view *query.Machine) *user_pb.Machine {
|
||||
return &user_pb.Machine{
|
||||
Name: view.Name,
|
||||
Description: view.Description,
|
||||
HasSecret: view.Secret != nil,
|
||||
HasSecret: view.EncodedSecret != "",
|
||||
AccessTokenType: AccessTokenTypeToPb(view.AccessTokenType),
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func machineToPb(userQ *query.Machine) *user.MachineUser {
|
||||
return &user.MachineUser{
|
||||
Name: userQ.Name,
|
||||
Description: userQ.Description,
|
||||
HasSecret: userQ.Secret != nil,
|
||||
HasSecret: userQ.EncodedSecret != "",
|
||||
AccessTokenType: accessTokenTypeToPb(userQ.AccessTokenType),
|
||||
}
|
||||
}
|
||||
|
@ -1050,8 +1050,13 @@ func (s *Server) verifyClientSecret(ctx context.Context, client *query.OIDCClien
|
||||
if secret == "" {
|
||||
return oidc.ErrInvalidClient().WithDescription("empty client secret")
|
||||
}
|
||||
if err = crypto.CompareHash(client.ClientSecret, []byte(secret), s.hashAlg); err != nil {
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "passwap.Verify")
|
||||
updated, err := s.hasher.Verify(client.HashedSecret, secret)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err != nil {
|
||||
s.command.OIDCSecretCheckFailed(ctx, client.AppID, client.ProjectID, client.Settings.ResourceOwner)
|
||||
return oidc.ErrInvalidClient().WithParent(err).WithDescription("invalid secret")
|
||||
}
|
||||
s.command.OIDCSecretCheckSucceeded(ctx, client.AppID, client.ProjectID, client.Settings.ResourceOwner, updated)
|
||||
return nil
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
@ -41,15 +41,18 @@ func (s *Server) clientCredentialsAuth(ctx context.Context, clientID, clientSecr
|
||||
if err != nil {
|
||||
return nil, err // defaults to server error
|
||||
}
|
||||
if user.Machine == nil || user.Machine.Secret == nil {
|
||||
if user.Machine == nil || user.Machine.EncodedSecret == "" {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "OIDC-pieP8", "Errors.User.Machine.Secret.NotExisting")
|
||||
}
|
||||
if err = crypto.CompareHash(user.Machine.Secret, []byte(clientSecret), s.hashAlg); err != nil {
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "passwap.Verify")
|
||||
updated, err := s.hasher.Verify(user.Machine.EncodedSecret, clientSecret)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err != nil {
|
||||
s.command.MachineSecretCheckFailed(ctx, user.ID, user.ResourceOwner)
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "OIDC-VoXo6", "Errors.User.Machine.Secret.Invalid")
|
||||
}
|
||||
|
||||
s.command.MachineSecretCheckSucceeded(ctx, user.ID, user.ResourceOwner)
|
||||
s.command.MachineSecretCheckSucceeded(ctx, user.ID, user.ResourceOwner, updated)
|
||||
return &clientCredentialsClient{
|
||||
id: clientID,
|
||||
user: user,
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -149,8 +148,8 @@ func (s *Server) introspectionClientAuth(ctx context.Context, cc *op.ClientCrede
|
||||
return client.ClientID, client.ProjectID, nil
|
||||
|
||||
}
|
||||
if client.ClientSecret != nil {
|
||||
if err := crypto.CompareHash(client.ClientSecret, []byte(cc.ClientSecret), s.hashAlg); err != nil {
|
||||
if client.HashedSecret != "" {
|
||||
if err := s.introspectionClientSecretAuth(ctx, client, cc.ClientSecret); err != nil {
|
||||
return "", "", oidc.ErrUnauthorizedClient().WithParent(err)
|
||||
}
|
||||
return client.ClientID, client.ProjectID, nil
|
||||
@ -167,6 +166,35 @@ func (s *Server) introspectionClientAuth(ctx context.Context, cc *op.ClientCrede
|
||||
}
|
||||
}
|
||||
|
||||
var errNoAppType = errors.New("introspection client without app type")
|
||||
|
||||
func (s *Server) introspectionClientSecretAuth(ctx context.Context, client *query.IntrospectionClient, secret string) error {
|
||||
var (
|
||||
successCommand func(ctx context.Context, appID, projectID, resourceOwner, updated string)
|
||||
failedCommand func(ctx context.Context, appID, projectID, resourceOwner string)
|
||||
)
|
||||
switch client.AppType {
|
||||
case query.AppTypeAPI:
|
||||
successCommand = s.command.APISecretCheckSucceeded
|
||||
failedCommand = s.command.APISecretCheckFailed
|
||||
case query.AppTypeOIDC:
|
||||
successCommand = s.command.OIDCSecretCheckSucceeded
|
||||
failedCommand = s.command.OIDCSecretCheckFailed
|
||||
default:
|
||||
return zerrors.ThrowInternal(errNoAppType, "OIDC-ooD5Ot", "Errors.Internal")
|
||||
}
|
||||
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "passwap.Verify")
|
||||
updated, err := s.hasher.Verify(client.HashedSecret, secret)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err != nil {
|
||||
failedCommand(ctx, client.AppID, client.ProjectID, client.ResourceOwner)
|
||||
return err
|
||||
}
|
||||
successCommand(ctx, client.AppID, client.ProjectID, client.ResourceOwner, updated)
|
||||
return nil
|
||||
}
|
||||
|
||||
// clientFromCredentials parses the client ID early,
|
||||
// and makes a single query for the client for either auth methods.
|
||||
func (s *Server) clientFromCredentials(ctx context.Context, cc *op.ClientCredentials) (client *query.IntrospectionClient, err error) {
|
||||
|
@ -80,6 +80,7 @@ type OPStorage struct {
|
||||
}
|
||||
|
||||
func NewServer(
|
||||
ctx context.Context,
|
||||
config Config,
|
||||
defaultLogoutRedirectURI string,
|
||||
externalSecure bool,
|
||||
@ -93,13 +94,14 @@ func NewServer(
|
||||
userAgentCookie, instanceHandler func(http.Handler) http.Handler,
|
||||
accessHandler *middleware.AccessInterceptor,
|
||||
fallbackLogger *slog.Logger,
|
||||
hashConfig crypto.HashConfig,
|
||||
) (*Server, error) {
|
||||
opConfig, err := createOPConfig(config, defaultLogoutRedirectURI, cryptoKey)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w")
|
||||
}
|
||||
storage := newStorage(config, command, query, repo, encryptionAlg, es, projections, externalSecure)
|
||||
keyCache := newPublicKeyCache(context.TODO(), config.PublicKeyCacheMaxAge, query.GetPublicKeyByID)
|
||||
keyCache := newPublicKeyCache(ctx, config.PublicKeyCacheMaxAge, query.GetPublicKeyByID)
|
||||
accessTokenKeySet := newOidcKeySet(keyCache, withKeyExpiryCheck(true))
|
||||
idTokenHintKeySet := newOidcKeySet(keyCache)
|
||||
|
||||
@ -119,7 +121,10 @@ func NewServer(
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "OIDC-DAtg3", "cannot create provider")
|
||||
}
|
||||
|
||||
hasher, err := hashConfig.NewHasher()
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "OIDC-Aij4e", "cannot create secret hasher")
|
||||
}
|
||||
server := &Server{
|
||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||
repo: repo,
|
||||
@ -133,7 +138,7 @@ func NewServer(
|
||||
defaultAccessTokenLifetime: config.DefaultAccessTokenLifetime,
|
||||
defaultIdTokenLifetime: config.DefaultIdTokenLifetime,
|
||||
fallbackLogger: fallbackLogger,
|
||||
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
||||
hasher: hasher,
|
||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||
assetAPIPrefix: assets.AssetAPI(externalSecure),
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ type Server struct {
|
||||
defaultIdTokenLifetime time.Duration
|
||||
|
||||
fallbackLogger *slog.Logger
|
||||
hashAlg crypto.HashAlgorithm
|
||||
hasher *crypto.Hasher
|
||||
signingKeyAlgorithm string
|
||||
assetAPIPrefix func(ctx context.Context) string
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -33,9 +34,10 @@ type Commands struct {
|
||||
|
||||
jobs sync.WaitGroup
|
||||
|
||||
checkPermission domain.PermissionCheck
|
||||
newCode cryptoCodeFunc
|
||||
newCodeWithDefault cryptoCodeWithDefaultFunc
|
||||
checkPermission domain.PermissionCheck
|
||||
newEncryptedCode encrypedCodeFunc
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
newHashedSecret hashedSecretFunc
|
||||
|
||||
eventstore *eventstore.Eventstore
|
||||
static static.Storage
|
||||
@ -49,8 +51,8 @@ type Commands struct {
|
||||
smtpEncryption crypto.EncryptionAlgorithm
|
||||
smsEncryption crypto.EncryptionAlgorithm
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
codeAlg crypto.HashAlgorithm
|
||||
userPasswordHasher *crypto.Hasher
|
||||
secretHasher *crypto.Hasher
|
||||
machineKeySize int
|
||||
applicationKeySize int
|
||||
domainVerificationAlg crypto.EncryptionAlgorithm
|
||||
@ -106,6 +108,15 @@ func StartCommands(
|
||||
idGenerator := id.SonyFlakeGenerator()
|
||||
// reuse the oidcEncryption to be able to handle both tokens in the interceptor later on
|
||||
sessionAlg := oidcEncryption
|
||||
|
||||
secretHasher, err := defaults.SecretHasher.NewHasher()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("secret hasher: %w", err)
|
||||
}
|
||||
userPasswordHasher, err := defaults.PasswordHasher.NewHasher()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("password hasher: %w", err)
|
||||
}
|
||||
repo = &Commands{
|
||||
eventstore: es,
|
||||
static: staticStore,
|
||||
@ -123,14 +134,20 @@ func StartCommands(
|
||||
smtpEncryption: smtpEncryption,
|
||||
smsEncryption: smsEncryption,
|
||||
userEncryption: userEncryption,
|
||||
userPasswordHasher: userPasswordHasher,
|
||||
secretHasher: secretHasher,
|
||||
machineKeySize: int(defaults.SecretGenerators.MachineKeySize),
|
||||
applicationKeySize: int(defaults.SecretGenerators.ApplicationKeySize),
|
||||
domainVerificationAlg: domainVerificationEncryption,
|
||||
domainVerificationGenerator: crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, domainVerificationEncryption),
|
||||
domainVerificationValidator: api_http.ValidateDomain,
|
||||
keyAlgorithm: oidcEncryption,
|
||||
certificateAlgorithm: samlEncryption,
|
||||
webauthnConfig: webAuthN,
|
||||
httpClient: httpClient,
|
||||
checkPermission: permissionCheck,
|
||||
newCode: newCryptoCode,
|
||||
newCodeWithDefault: newCryptoCodeWithDefaultConfig,
|
||||
newEncryptedCode: newEncryptedCode,
|
||||
newEncryptedCodeWithDefault: newEncryptedCodeWithDefaultConfig,
|
||||
sessionTokenCreator: sessionTokenCreator(idGenerator, sessionAlg),
|
||||
sessionTokenVerifier: sessionTokenVerifier,
|
||||
defaultAccessTokenLifetime: defaultAccessTokenLifetime,
|
||||
@ -145,25 +162,17 @@ func StartCommands(
|
||||
GrpcServiceExisting: func(service string) bool { return false },
|
||||
GrpcMethodExisting: func(method string) bool { return false },
|
||||
ActionFunctionExisting: domain.FunctionExists(),
|
||||
}
|
||||
|
||||
repo.codeAlg = crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost)
|
||||
repo.userPasswordHasher, err = defaults.PasswordHasher.PasswordHasher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repo.machineKeySize = int(defaults.SecretGenerators.MachineKeySize)
|
||||
repo.applicationKeySize = int(defaults.SecretGenerators.ApplicationKeySize)
|
||||
|
||||
repo.multifactors = domain.MultifactorConfigs{
|
||||
OTP: domain.OTPConfig{
|
||||
CryptoMFA: otpEncryption,
|
||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||
multifactors: domain.MultifactorConfigs{
|
||||
OTP: domain.OTPConfig{
|
||||
CryptoMFA: otpEncryption,
|
||||
Issuer: defaults.Multifactors.OTP.Issuer,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
repo.domainVerificationGenerator = crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, repo.domainVerificationAlg)
|
||||
repo.domainVerificationValidator = api_http.ValidateDomain
|
||||
if defaultSecretGenerators != nil && defaultSecretGenerators.ClientSecret != nil {
|
||||
repo.newHashedSecret = newHashedSecretWithDefault(secretHasher, defaultSecretGenerators.ClientSecret)
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
|
@ -7,27 +7,26 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type cryptoCodeFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCode, error)
|
||||
type encrypedCodeFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error)
|
||||
|
||||
type cryptoCodeWithDefaultFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, defaultConfig *crypto.GeneratorConfig) (*CryptoCode, error)
|
||||
type encryptedCodeWithDefaultFunc func(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, defaultConfig *crypto.GeneratorConfig) (*EncryptedCode, error)
|
||||
|
||||
var emptyConfig = &crypto.GeneratorConfig{}
|
||||
|
||||
type CryptoCode struct {
|
||||
type EncryptedCode struct {
|
||||
Crypted *crypto.CryptoValue
|
||||
Plain string
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
func newCryptoCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCode, error) {
|
||||
return newCryptoCodeWithDefaultConfig(ctx, filter, typ, alg, emptyConfig)
|
||||
func newEncryptedCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return newEncryptedCodeWithDefaultConfig(ctx, filter, typ, alg, emptyConfig)
|
||||
}
|
||||
|
||||
func newCryptoCodeWithDefaultConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, defaultConfig *crypto.GeneratorConfig) (*CryptoCode, error) {
|
||||
gen, config, err := secretGenerator(ctx, filter, typ, alg, defaultConfig)
|
||||
func newEncryptedCodeWithDefaultConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, defaultConfig *crypto.GeneratorConfig) (*EncryptedCode, error) {
|
||||
gen, config, err := encryptedCodeGenerator(ctx, filter, typ, alg, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -35,41 +34,47 @@ func newCryptoCodeWithDefaultConfig(ctx context.Context, filter preparation.Filt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CryptoCode{
|
||||
return &EncryptedCode{
|
||||
Crypted: crypted,
|
||||
Plain: plain,
|
||||
Expiry: config.Expiry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func verifyCryptoCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, creation time.Time, expiry time.Duration, crypted *crypto.CryptoValue, plain string) error {
|
||||
gen, _, err := secretGenerator(ctx, filter, typ, alg, emptyConfig)
|
||||
func verifyEncryptedCode(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, creation time.Time, expiry time.Duration, crypted *crypto.CryptoValue, plain string) error {
|
||||
gen, _, err := encryptedCodeGenerator(ctx, filter, typ, alg, emptyConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return crypto.VerifyCode(creation, expiry, crypted, plain, gen)
|
||||
return crypto.VerifyCode(creation, expiry, crypted, plain, gen.Alg())
|
||||
}
|
||||
|
||||
func secretGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.Crypto, defaultConfig *crypto.GeneratorConfig) (crypto.Generator, *crypto.GeneratorConfig, error) {
|
||||
config, err := secretGeneratorConfigWithDefault(ctx, filter, typ, defaultConfig)
|
||||
func encryptedCodeGenerator(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, defaultConfig *crypto.GeneratorConfig) (crypto.Generator, *crypto.GeneratorConfig, error) {
|
||||
config, err := cryptoGeneratorConfigWithDefault(ctx, filter, typ, defaultConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
switch a := alg.(type) {
|
||||
case crypto.HashAlgorithm:
|
||||
return crypto.NewHashGenerator(*config, a), config, nil
|
||||
case crypto.EncryptionAlgorithm:
|
||||
return crypto.NewEncryptionGenerator(*config, a), config, nil
|
||||
default:
|
||||
return nil, nil, zerrors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", a)
|
||||
return crypto.NewEncryptionGenerator(*config, alg), config, nil
|
||||
}
|
||||
|
||||
type hashedSecretFunc func(ctx context.Context, filter preparation.FilterToQueryReducer) (encodedHash, plain string, err error)
|
||||
|
||||
func newHashedSecretWithDefault(hasher *crypto.Hasher, defaultConfig *crypto.GeneratorConfig) hashedSecretFunc {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) (encodedHash string, plain string, err error) {
|
||||
config, err := cryptoGeneratorConfigWithDefault(ctx, filter, domain.SecretGeneratorTypeAppSecret, defaultConfig)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
generator := crypto.NewHashGenerator(*config, hasher)
|
||||
return generator.NewCode()
|
||||
}
|
||||
}
|
||||
|
||||
func secretGeneratorConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType) (*crypto.GeneratorConfig, error) {
|
||||
return secretGeneratorConfigWithDefault(ctx, filter, typ, emptyConfig)
|
||||
func cryptoGeneratorConfig(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType) (*crypto.GeneratorConfig, error) {
|
||||
return cryptoGeneratorConfigWithDefault(ctx, filter, typ, emptyConfig)
|
||||
}
|
||||
|
||||
func secretGeneratorConfigWithDefault(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, defaultConfig *crypto.GeneratorConfig) (*crypto.GeneratorConfig, error) {
|
||||
func cryptoGeneratorConfigWithDefault(ctx context.Context, filter preparation.FilterToQueryReducer, typ domain.SecretGeneratorType, defaultConfig *crypto.GeneratorConfig) (*crypto.GeneratorConfig, error) {
|
||||
wm := NewInstanceSecretGeneratorConfigWriteModel(ctx, typ)
|
||||
events, err := filter(ctx, wm.Query())
|
||||
if err != nil {
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/passwap"
|
||||
"github.com/zitadel/passwap/bcrypt"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
@ -15,12 +17,11 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func mockCode(code string, exp time.Duration) cryptoCodeFunc {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.Crypto) (*CryptoCode, error) {
|
||||
return &CryptoCode{
|
||||
func mockEncryptedCode(code string, exp time.Duration) encrypedCodeFunc {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return &EncryptedCode{
|
||||
Crypted: &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
@ -33,9 +34,9 @@ func mockCode(code string, exp time.Duration) cryptoCodeFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func mockCodeWithDefault(code string, exp time.Duration) cryptoCodeWithDefaultFunc {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.Crypto, _ *crypto.GeneratorConfig) (*CryptoCode, error) {
|
||||
return &CryptoCode{
|
||||
func mockEncryptedCodeWithDefault(code string, exp time.Duration) encryptedCodeWithDefaultFunc {
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer, _ domain.SecretGeneratorType, alg crypto.EncryptionAlgorithm, _ *crypto.GeneratorConfig) (*EncryptedCode, error) {
|
||||
return &EncryptedCode{
|
||||
Crypted: &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
@ -48,6 +49,12 @@ func mockCodeWithDefault(code string, exp time.Duration) cryptoCodeWithDefaultFu
|
||||
}
|
||||
}
|
||||
|
||||
func mockHashedSecret(secret string) hashedSecretFunc {
|
||||
return func(_ context.Context, _ preparation.FilterToQueryReducer) (encodedHash string, plain string, err error) {
|
||||
return secret, secret, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
testGeneratorConfig = crypto.GeneratorConfig{
|
||||
Length: 12,
|
||||
@ -74,7 +81,7 @@ func testSecretGeneratorAddedEvent(typ domain.SecretGeneratorType) *instance.Sec
|
||||
func Test_newCryptoCode(t *testing.T) {
|
||||
type args struct {
|
||||
typ domain.SecretGeneratorType
|
||||
alg crypto.Crypto
|
||||
alg crypto.EncryptionAlgorithm
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -87,7 +94,7 @@ func Test_newCryptoCode(t *testing.T) {
|
||||
eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
@ -98,13 +105,13 @@ func Test_newCryptoCode(t *testing.T) {
|
||||
)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := newCryptoCode(context.Background(), tt.eventstore.Filter, tt.args.typ, tt.args.alg)
|
||||
got, err := newEncryptedCode(context.Background(), tt.eventstore.Filter, tt.args.typ, tt.args.alg) //nolint:staticcheck
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
if tt.wantErr == nil {
|
||||
require.NotNil(t, got)
|
||||
@ -120,12 +127,12 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
es := eventstoreExpect(t, expectFilter(
|
||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
||||
))
|
||||
code, err := newCryptoCode(context.Background(), es.Filter, domain.SecretGeneratorTypeVerifyEmailCode, crypto.CreateMockHashAlg(gomock.NewController(t)))
|
||||
code, err := newEncryptedCode(context.Background(), es.Filter, domain.SecretGeneratorTypeVerifyEmailCode, crypto.CreateMockEncryptionAlg(gomock.NewController(t))) //nolint:staticcheck
|
||||
require.NoError(t, err)
|
||||
|
||||
type args struct {
|
||||
typ domain.SecretGeneratorType
|
||||
alg crypto.Crypto
|
||||
alg crypto.EncryptionAlgorithm
|
||||
expiry time.Duration
|
||||
crypted *crypto.CryptoValue
|
||||
plain string
|
||||
@ -141,7 +148,7 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
expiry: code.Expiry,
|
||||
crypted: code.Crypted,
|
||||
plain: code.Plain,
|
||||
@ -155,7 +162,7 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
expiry: code.Expiry,
|
||||
crypted: code.Crypted,
|
||||
plain: code.Plain,
|
||||
@ -168,7 +175,7 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
expiry: code.Expiry,
|
||||
crypted: code.Crypted,
|
||||
plain: "wrong",
|
||||
@ -178,7 +185,7 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := verifyCryptoCode(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, time.Now(), tt.args.expiry, tt.args.crypted, tt.args.plain)
|
||||
err := verifyEncryptedCode(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, time.Now(), tt.args.expiry, tt.args.crypted, tt.args.plain) //nolint:staticcheck
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
@ -188,10 +195,10 @@ func Test_verifyCryptoCode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_secretGenerator(t *testing.T) {
|
||||
func Test_cryptoCodeGenerator(t *testing.T) {
|
||||
type args struct {
|
||||
typ domain.SecretGeneratorType
|
||||
alg crypto.Crypto
|
||||
alg crypto.EncryptionAlgorithm
|
||||
defaultConfig *crypto.GeneratorConfig
|
||||
}
|
||||
tests := []struct {
|
||||
@ -207,24 +214,11 @@ func Test_secretGenerator(t *testing.T) {
|
||||
eventsore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
defaultConfig: emptyConfig,
|
||||
},
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "hash generator",
|
||||
eventsore: eventstoreExpect(t, expectFilter(
|
||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
||||
)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
defaultConfig: emptyConfig,
|
||||
},
|
||||
want: crypto.NewHashGenerator(testGeneratorConfig, crypto.CreateMockHashAlg(gomock.NewController(t))),
|
||||
wantConf: &testGeneratorConfig,
|
||||
},
|
||||
{
|
||||
name: "encryption generator",
|
||||
eventsore: eventstoreExpect(t, expectFilter(
|
||||
@ -238,17 +232,6 @@ func Test_secretGenerator(t *testing.T) {
|
||||
want: crypto.NewEncryptionGenerator(testGeneratorConfig, crypto.CreateMockEncryptionAlg(gomock.NewController(t))),
|
||||
wantConf: &testGeneratorConfig,
|
||||
},
|
||||
{
|
||||
name: "hash generator with default config",
|
||||
eventsore: eventstoreExpect(t, expectFilter()),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
defaultConfig: &testGeneratorConfig,
|
||||
},
|
||||
want: crypto.NewHashGenerator(testGeneratorConfig, crypto.CreateMockHashAlg(gomock.NewController(t))),
|
||||
wantConf: &testGeneratorConfig,
|
||||
},
|
||||
{
|
||||
name: "encryption generator with default config",
|
||||
eventsore: eventstoreExpect(t, expectFilter()),
|
||||
@ -260,25 +243,79 @@ func Test_secretGenerator(t *testing.T) {
|
||||
want: crypto.NewEncryptionGenerator(testGeneratorConfig, crypto.CreateMockEncryptionAlg(gomock.NewController(t))),
|
||||
wantConf: &testGeneratorConfig,
|
||||
},
|
||||
{
|
||||
name: "unsupported type",
|
||||
eventsore: eventstoreExpect(t, expectFilter(
|
||||
eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypeVerifyEmailCode)),
|
||||
)),
|
||||
args: args{
|
||||
typ: domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
alg: nil,
|
||||
defaultConfig: emptyConfig,
|
||||
},
|
||||
wantErr: zerrors.ThrowInternalf(nil, "COMMA-RreV6", "Errors.Internal unsupported crypto algorithm type %T", nil),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, gotConf, err := secretGenerator(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, tt.args.defaultConfig)
|
||||
got, gotConf, err := encryptedCodeGenerator(context.Background(), tt.eventsore.Filter, tt.args.typ, tt.args.alg, tt.args.defaultConfig) //nolint:staticcheck
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.IsType(t, tt.want, got)
|
||||
assert.Equal(t, tt.wantConf, gotConf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_newHashedSecretWithDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
wantLen int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "filter error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "default config",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
wantLen: 32,
|
||||
},
|
||||
{
|
||||
name: "instance config",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSecretGeneratorAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
domain.SecretGeneratorTypeAppSecret,
|
||||
24,
|
||||
time.Hour*1,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
wantLen: 24,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hasher := &crypto.Hasher{
|
||||
Swapper: passwap.NewSwapper(bcrypt.New(bcrypt.MinCost)),
|
||||
}
|
||||
defaultConfig := &crypto.GeneratorConfig{
|
||||
Length: 32,
|
||||
Expiry: time.Minute,
|
||||
IncludeLowerLetters: true,
|
||||
}
|
||||
generate := newHashedSecretWithDefault(hasher, defaultConfig)
|
||||
encodedHash, plain, err := generate(context.Background(), tt.eventstore(t).Filter) //nolint:staticcheck
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, plain, tt.wantLen)
|
||||
_, err = hasher.Verify(encodedHash, plain)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,6 @@ func (e *Email) Validate() error {
|
||||
return e.Address.Validate()
|
||||
}
|
||||
|
||||
func (c *Commands) newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCode, error) {
|
||||
return c.newCode(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg)
|
||||
func (c *Commands) newEmailCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypeVerifyEmailCode, alg)
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func (wm *OAuthIDPWriteModel) NewChanges(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint,
|
||||
@ -278,7 +278,7 @@ func (wm *OIDCIDPWriteModel) NewChanges(
|
||||
issuer,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
idTokenMapping bool,
|
||||
options idp.Options,
|
||||
@ -636,7 +636,7 @@ func (wm *AzureADIDPWriteModel) NewChanges(
|
||||
name string,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
@ -772,7 +772,7 @@ func (wm *GitHubIDPWriteModel) NewChanges(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) ([]idp.GitHubIDPChanges, error) {
|
||||
@ -904,7 +904,7 @@ func (wm *GitHubEnterpriseIDPWriteModel) NewChanges(
|
||||
name,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint string,
|
||||
@ -1037,7 +1037,7 @@ func (wm *GitLabIDPWriteModel) NewChanges(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) ([]idp.GitLabIDPChanges, error) {
|
||||
@ -1161,7 +1161,7 @@ func (wm *GitLabSelfHostedIDPWriteModel) NewChanges(
|
||||
issuer string,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) ([]idp.GitLabSelfHostedIDPChanges, error) {
|
||||
@ -1285,7 +1285,7 @@ func (wm *GoogleIDPWriteModel) NewChanges(
|
||||
name string,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) ([]idp.GoogleIDPChanges, error) {
|
||||
@ -1460,7 +1460,7 @@ func (wm *LDAPIDPWriteModel) NewChanges(
|
||||
userObjectClasses []string,
|
||||
userFilters []string,
|
||||
timeout time.Duration,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
attributes idp.LDAPAttributes,
|
||||
options idp.Options,
|
||||
) ([]idp.LDAPIDPChanges, error) {
|
||||
@ -1653,7 +1653,7 @@ func (wm *AppleIDPWriteModel) NewChanges(
|
||||
teamID string,
|
||||
keyID string,
|
||||
privateKey []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) ([]idp.AppleIDPChanges, error) {
|
||||
@ -1790,7 +1790,7 @@ func (wm *SAMLIDPWriteModel) NewChanges(
|
||||
metadata,
|
||||
key,
|
||||
certificate []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
binding string,
|
||||
withSignedRequest bool,
|
||||
options idp.Options,
|
||||
|
@ -126,7 +126,6 @@ type SetQuotas struct {
|
||||
}
|
||||
|
||||
type SecretGenerators struct {
|
||||
PasswordSaltCost uint
|
||||
ClientSecret *crypto.GeneratorConfig
|
||||
InitializeUserCode *crypto.GeneratorConfig
|
||||
EmailVerificationCode *crypto.GeneratorConfig
|
||||
@ -457,7 +456,6 @@ func setupMinimalInterfaces(commands *Commands, validations *[]preparation.Valid
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
|
||||
commands.AddAPIAppCommand(
|
||||
@ -469,7 +467,6 @@ func setupMinimalInterfaces(commands *Commands, validations *[]preparation.Valid
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
|
||||
commands.AddAPIAppCommand(
|
||||
@ -481,10 +478,9 @@ func setupMinimalInterfaces(commands *Commands, validations *[]preparation.Valid
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
nil,
|
||||
),
|
||||
|
||||
commands.AddOIDCAppCommand(cnsl, nil),
|
||||
commands.AddOIDCAppCommand(cnsl),
|
||||
SetIAMConsoleID(instanceAgg, &cnsl.ClientID, &ids.consoleAppID),
|
||||
)
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
@ -141,12 +140,7 @@ func TestCommandSide_AddInstanceDomain(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"consoleApplicationID",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
|
@ -61,7 +61,7 @@ func (wm *InstanceOAuthIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint,
|
||||
@ -171,7 +171,7 @@ func (wm *InstanceOIDCIDPWriteModel) NewChangedEvent(
|
||||
issuer,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
idTokenMapping bool,
|
||||
options idp.Options,
|
||||
@ -344,7 +344,7 @@ func (wm *InstanceAzureADIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
@ -420,7 +420,7 @@ func (wm *InstanceGitHubIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*instance.GitHubIDPChangedEvent, error) {
|
||||
@ -485,7 +485,7 @@ func (wm *InstanceGitHubEnterpriseIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint string,
|
||||
@ -563,7 +563,7 @@ func (wm *InstanceGitLabIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*instance.GitLabIDPChangedEvent, error) {
|
||||
@ -629,7 +629,7 @@ func (wm *InstanceGitLabSelfHostedIDPWriteModel) NewChangedEvent(
|
||||
issuer,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*instance.GitLabSelfHostedIDPChangedEvent, error) {
|
||||
@ -695,7 +695,7 @@ func (wm *InstanceGoogleIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*instance.GoogleIDPChangedEvent, error) {
|
||||
@ -767,7 +767,7 @@ func (wm *InstanceLDAPIDPWriteModel) NewChangedEvent(
|
||||
userObjectClasses []string,
|
||||
userFilters []string,
|
||||
timeout time.Duration,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
attributes idp.LDAPAttributes,
|
||||
options idp.Options,
|
||||
) (*instance.LDAPIDPChangedEvent, error) {
|
||||
@ -848,7 +848,7 @@ func (wm *InstanceAppleIDPWriteModel) NewChangedEvent(
|
||||
teamID,
|
||||
keyID string,
|
||||
privateKey []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*instance.AppleIDPChangedEvent, error) {
|
||||
@ -912,7 +912,7 @@ func (wm *InstanceSAMLIDPWriteModel) NewChangedEvent(
|
||||
metadata,
|
||||
key,
|
||||
certificate []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
binding string,
|
||||
withSignedRequest bool,
|
||||
options idp.Options,
|
||||
|
@ -93,7 +93,7 @@ func (wm *InstanceIDPOIDCConfigWriteModel) NewChangedEvent(
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
idpDisplayNameMapping,
|
||||
userNameMapping domain.OIDCMappingField,
|
||||
scopes ...string,
|
||||
|
@ -279,8 +279,8 @@ func (h plainHasher) Verify(encoded, password string) (verifier.Result, error) {
|
||||
//
|
||||
// With `x` set to "foo", the following encoded string would be produced by Hash:
|
||||
// $plain$foo$password
|
||||
func mockPasswordHasher(x string) *crypto.PasswordHasher {
|
||||
return &crypto.PasswordHasher{
|
||||
func mockPasswordHasher(x string) *crypto.Hasher {
|
||||
return &crypto.Hasher{
|
||||
Swapper: passwap.NewSwapper(plainHasher{x: x}),
|
||||
Prefixes: []string{"$plain$"},
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func (wm *OrgOAuthIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint,
|
||||
@ -173,7 +173,7 @@ func (wm *OrgOIDCIDPWriteModel) NewChangedEvent(
|
||||
issuer,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
idTokenMapping bool,
|
||||
options idp.Options,
|
||||
@ -350,7 +350,7 @@ func (wm *OrgAzureADIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
@ -426,7 +426,7 @@ func (wm *OrgGitHubIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*org.GitHubIDPChangedEvent, error) {
|
||||
@ -492,7 +492,7 @@ func (wm *OrgGitHubEnterpriseIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
userEndpoint string,
|
||||
@ -571,7 +571,7 @@ func (wm *OrgGitLabIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*org.GitLabIDPChangedEvent, error) {
|
||||
@ -637,7 +637,7 @@ func (wm *OrgGitLabSelfHostedIDPWriteModel) NewChangedEvent(
|
||||
issuer,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*org.GitLabSelfHostedIDPChangedEvent, error) {
|
||||
@ -705,7 +705,7 @@ func (wm *OrgGoogleIDPWriteModel) NewChangedEvent(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*org.GoogleIDPChangedEvent, error) {
|
||||
@ -777,7 +777,7 @@ func (wm *OrgLDAPIDPWriteModel) NewChangedEvent(
|
||||
userObjectClasses []string,
|
||||
userFilters []string,
|
||||
timeout time.Duration,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
attributes idp.LDAPAttributes,
|
||||
options idp.Options,
|
||||
) (*org.LDAPIDPChangedEvent, error) {
|
||||
@ -858,7 +858,7 @@ func (wm *OrgAppleIDPWriteModel) NewChangedEvent(
|
||||
teamID,
|
||||
keyID string,
|
||||
privateKey []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
scopes []string,
|
||||
options idp.Options,
|
||||
) (*org.AppleIDPChangedEvent, error) {
|
||||
@ -924,7 +924,7 @@ func (wm *OrgSAMLIDPWriteModel) NewChangedEvent(
|
||||
metadata,
|
||||
key,
|
||||
certificate []byte,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
binding string,
|
||||
withSignedRequest bool,
|
||||
options idp.Options,
|
||||
|
@ -92,7 +92,7 @@ func (wm *IDPOIDCConfigWriteModel) NewChangedEvent(
|
||||
authorizationEndpoint,
|
||||
tokenEndpoint,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
secretCrypto crypto.EncryptionAlgorithm,
|
||||
idpDisplayNameMapping,
|
||||
userNameMapping domain.OIDCMappingField,
|
||||
scopes ...string,
|
||||
|
@ -1260,7 +1260,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
keyAlgorithm crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
@ -1437,7 +1437,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID", "userID"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithRequestedDomain(context.Background(), "iam-domain"),
|
||||
@ -1616,7 +1616,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID", "userID", "tokenID"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
@ -1669,10 +1669,10 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newCode: tt.fields.newCode,
|
||||
keyAlgorithm: tt.fields.keyAlgorithm,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
keyAlgorithm: tt.fields.keyAlgorithm,
|
||||
zitadelRoles: []authz.RoleMapping{
|
||||
{
|
||||
Role: domain.RoleOrgOwner,
|
||||
|
@ -16,6 +16,6 @@ type Phone struct {
|
||||
ReturnCode bool
|
||||
}
|
||||
|
||||
func (c *Commands) newPhoneCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCode, error) {
|
||||
return c.newCode(ctx, filter, domain.SecretGeneratorTypeVerifyPhoneCode, alg)
|
||||
func (c *Commands) newPhoneCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypeVerifyPhoneCode, alg)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ type CommandVerifier interface {
|
||||
Validate(eventstore.Command) bool
|
||||
}
|
||||
|
||||
// AssertValidation checks if the validation works as inteded
|
||||
// AssertValidation checks if the validation works as intended
|
||||
func AssertValidation(t *testing.T, ctx context.Context, validation preparation.Validation, filter preparation.FilterToQueryReducer, want Want) {
|
||||
t.Helper()
|
||||
|
||||
@ -51,7 +51,7 @@ func AssertValidation(t *testing.T, ctx context.Context, validation preparation.
|
||||
for i, cmd := range want.Commands {
|
||||
if v, ok := cmd.(CommandVerifier); ok {
|
||||
if verified := v.Validate(cmds[i]); !verified {
|
||||
t.Errorf("verification failed on command: = %v, want %v", cmds[i], cmd)
|
||||
t.Errorf("verification failed on command: =\n%v\nwant\n%v", cmds[i], cmd)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/repository/project"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -16,10 +14,6 @@ type AddApp struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c *Commands) newAppClientSecret(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.HashAlgorithm) (*CryptoCode, error) {
|
||||
return c.newCode(ctx, filter, domain.SecretGeneratorTypeAppSecret, alg)
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeApplication(ctx context.Context, projectID string, appChange domain.Application, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if projectID == "" || appChange.GetAppID() == "" || appChange.GetApplicationName() == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4m9vS", "Errors.Project.App.Invalid")
|
||||
|
@ -4,10 +4,7 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
project_repo "github.com/zitadel/zitadel/internal/repository/project"
|
||||
@ -20,11 +17,11 @@ type addAPIApp struct {
|
||||
AuthMethodType domain.APIAuthMethodType
|
||||
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
EncodedHash string
|
||||
ClientSecretPlain string
|
||||
}
|
||||
|
||||
func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashAlgorithm) preparation.Validation {
|
||||
func (c *Commands) AddAPIAppCommand(app *addAPIApp) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if app.ID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "PROJE-XHsKt", "Errors.Invalid.Argument")
|
||||
@ -44,11 +41,10 @@ func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashA
|
||||
}
|
||||
|
||||
if app.AuthMethodType == domain.APIAuthMethodTypeBasic {
|
||||
code, err := c.newAppClientSecret(ctx, filter, clientSecretAlg)
|
||||
app.EncodedHash, app.ClientSecretPlain, err = c.newHashedSecret(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.ClientSecret, app.ClientSecretPlain = code.Crypted, code.Plain
|
||||
}
|
||||
|
||||
return []eventstore.Command{
|
||||
@ -63,7 +59,7 @@ func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashA
|
||||
&app.Aggregate.Aggregate,
|
||||
app.ID,
|
||||
app.ClientID,
|
||||
app.ClientSecret,
|
||||
app.EncodedHash,
|
||||
app.AuthMethodType,
|
||||
),
|
||||
}, nil
|
||||
@ -71,7 +67,7 @@ func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashA
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) AddAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
func (c *Commands) AddAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner, appID string) (_ *domain.APIApp, err error) {
|
||||
existingAPI, err := c.getAPIAppWriteModel(ctx, apiApp.AggregateID, appID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -84,10 +80,10 @@ func (c *Commands) AddAPIApplicationWithID(ctx context.Context, apiApp *domain.A
|
||||
return nil, zerrors.ThrowPreconditionFailed(err, "PROJECT-9fnsa", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (_ *domain.APIApp, err error) {
|
||||
if apiApp == nil || apiApp.AggregateID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Project.App.Invalid")
|
||||
}
|
||||
@ -105,10 +101,10 @@ func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
|
||||
func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, project *domain.Project, appID string) (_ *domain.APIApp, err error) {
|
||||
apiApp.AppID = appID
|
||||
|
||||
addedApplication := NewAPIApplicationWriteModel(apiApp.AggregateID, resourceOwner)
|
||||
@ -118,12 +114,14 @@ func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.A
|
||||
project_repo.NewApplicationAddedEvent(ctx, projectAgg, apiApp.AppID, apiApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
var plain string
|
||||
err = domain.SetNewClientID(apiApp, c.idGenerator, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(apiApp, appSecretGenerator)
|
||||
plain, err = domain.SetNewClientSecretIfNeeded(apiApp, func() (string, string, error) {
|
||||
return c.newHashedSecret(ctx, c.eventstore.Filter) //nolint:staticcheck
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -131,7 +129,7 @@ func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.A
|
||||
projectAgg,
|
||||
apiApp.AppID,
|
||||
apiApp.ClientID,
|
||||
apiApp.ClientSecret,
|
||||
apiApp.EncodedHash,
|
||||
apiApp.AuthMethodType))
|
||||
|
||||
addedApplication.AppID = apiApp.AppID
|
||||
@ -144,7 +142,7 @@ func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.A
|
||||
return nil, err
|
||||
}
|
||||
result := apiWriteModelToAPIConfig(addedApplication)
|
||||
result.ClientSecretString = stringPw
|
||||
result.ClientSecretString = plain
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -188,7 +186,7 @@ func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIA
|
||||
return apiWriteModelToAPIConfig(existingAPI), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string, appSecretGenerator crypto.Generator) (*domain.APIApp, error) {
|
||||
func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.APIApp, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
}
|
||||
@ -203,14 +201,14 @@ func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, ap
|
||||
if !existingAPI.IsAPI() {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-aeH4", "Errors.Project.App.IsNotAPI")
|
||||
}
|
||||
cryptoSecret, stringPW, err := domain.NewClientSecret(appSecretGenerator)
|
||||
encodedHash, plain, err := c.newHashedSecret(ctx, c.eventstore.Filter) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingAPI.WriteModel)
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, project_repo.NewAPIConfigSecretChangedEvent(ctx, projectAgg, appID, cryptoSecret))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, project_repo.NewAPIConfigSecretChangedEvent(ctx, projectAgg, appID, encodedHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -220,7 +218,7 @@ func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, ap
|
||||
}
|
||||
|
||||
result := apiWriteModelToAPIConfig(existingAPI)
|
||||
result.ClientSecretString = stringPW
|
||||
result.ClientSecretString = plain
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -233,26 +231,35 @@ func (c *Commands) VerifyAPIClientSecret(ctx context.Context, projectID, appID,
|
||||
return err
|
||||
}
|
||||
if !app.State.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-DFnbf", "Errors.Project.App.NoExisting")
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-DFnbf", "Errors.Project.App.NotExisting")
|
||||
}
|
||||
if !app.IsAPI() {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-Bf3fw", "Errors.Project.App.IsNotAPI")
|
||||
}
|
||||
if app.ClientSecret == nil {
|
||||
if app.HashedSecret == "" {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-D3t5g", "Errors.Project.App.APIConfigInvalid")
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&app.WriteModel)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
err = crypto.CompareHash(app.ClientSecret, []byte(secret), c.codeAlg)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "passwap.Verify")
|
||||
updated, err := c.secretHasher.Verify(app.HashedSecret, secret)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err == nil {
|
||||
_, err = c.eventstore.Push(ctx, project_repo.NewAPIConfigSecretCheckSucceededEvent(ctx, projectAgg, app.AppID))
|
||||
c.apiSecretCheckSucceeded(ctx, projectAgg, app.AppID, updated)
|
||||
return err
|
||||
}
|
||||
_, err = c.eventstore.Push(ctx, project_repo.NewAPIConfigSecretCheckFailedEvent(ctx, projectAgg, app.AppID))
|
||||
logging.Log("COMMAND-g3f12").OnError(err).Error("could not push event APIClientSecretCheckFailed")
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-SADfg", "Errors.Project.App.ClientSecretInvalid")
|
||||
c.apiSecretCheckFailed(ctx, projectAgg, app.AppID)
|
||||
return zerrors.ThrowInvalidArgument(err, "COMMAND-SADfg", "Errors.Project.App.ClientSecretInvalid")
|
||||
}
|
||||
|
||||
func (c *Commands) APISecretCheckSucceeded(ctx context.Context, appID, projectID, resourceOwner, updated string) {
|
||||
agg := project_repo.NewAggregate(projectID, resourceOwner)
|
||||
c.apiSecretCheckSucceeded(ctx, &agg.Aggregate, appID, updated)
|
||||
}
|
||||
|
||||
func (c *Commands) APISecretCheckFailed(ctx context.Context, appID, projectID, resourceOwner string) {
|
||||
agg := project_repo.NewAggregate(projectID, resourceOwner)
|
||||
c.apiSecretCheckFailed(ctx, &agg.Aggregate, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) getAPIAppWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*APIApplicationWriteModel, error) {
|
||||
@ -263,3 +270,18 @@ func (c *Commands) getAPIAppWriteModel(ctx context.Context, projectID, appID, re
|
||||
}
|
||||
return appWriteModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) apiSecretCheckSucceeded(ctx context.Context, agg *eventstore.Aggregate, appID, updated string) {
|
||||
cmds := append(
|
||||
make([]eventstore.Command, 0, 2),
|
||||
project_repo.NewAPIConfigSecretCheckSucceededEvent(ctx, agg, appID),
|
||||
)
|
||||
if updated != "" {
|
||||
cmds = append(cmds, project_repo.NewAPIConfigSecretHashUpdatedEvent(ctx, agg, appID, updated))
|
||||
}
|
||||
c.asyncPush(ctx, cmds...)
|
||||
}
|
||||
|
||||
func (c *Commands) apiSecretCheckFailed(ctx context.Context, agg *eventstore.Aggregate, appID string) {
|
||||
c.asyncPush(ctx, project_repo.NewAPIConfigSecretCheckFailedEvent(ctx, agg, appID))
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ type APIApplicationWriteModel struct {
|
||||
AppID string
|
||||
AppName string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
HashedSecret string
|
||||
ClientSecretString string
|
||||
AuthMethodType domain.APIAuthMethodType
|
||||
State domain.AppState
|
||||
@ -83,6 +83,11 @@ func (wm *APIApplicationWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigSecretHashUpdatedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
@ -114,7 +119,9 @@ func (wm *APIApplicationWriteModel) Reduce() error {
|
||||
case *project.APIConfigChangedEvent:
|
||||
wm.appendChangeAPIEvent(e)
|
||||
case *project.APIConfigSecretChangedEvent:
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.HashedSecret = crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)
|
||||
case *project.APIConfigSecretHashUpdatedEvent:
|
||||
wm.HashedSecret = e.HashedSecret
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
}
|
||||
@ -125,7 +132,7 @@ func (wm *APIApplicationWriteModel) Reduce() error {
|
||||
func (wm *APIApplicationWriteModel) appendAddAPIEvent(e *project.APIConfigAddedEvent) {
|
||||
wm.api = true
|
||||
wm.ClientID = e.ClientID
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.HashedSecret = crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)
|
||||
wm.AuthMethodType = e.AuthMethodType
|
||||
}
|
||||
|
||||
@ -150,8 +157,9 @@ func (wm *APIApplicationWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
project.APIConfigAddedType,
|
||||
project.APIConfigChangedType,
|
||||
project.APIConfigSecretChangedType,
|
||||
project.ProjectRemovedType).
|
||||
Builder()
|
||||
project.APIConfigSecretHashUpdatedType,
|
||||
project.ProjectRemovedType,
|
||||
).Builder()
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) NewChangedEvent(
|
||||
|
@ -2,9 +2,13 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/passwap"
|
||||
"github.com/zitadel/passwap/bcrypt"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
@ -114,7 +118,7 @@ func TestAddAPIConfig(t *testing.T) {
|
||||
project.NewAPIConfigAddedEvent(ctx, &agg.Aggregate,
|
||||
"appID",
|
||||
"clientID@project",
|
||||
nil,
|
||||
"",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
),
|
||||
},
|
||||
@ -137,7 +141,6 @@ func TestAddAPIConfig(t *testing.T) {
|
||||
},
|
||||
AuthMethodType: domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
nil,
|
||||
), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
@ -145,14 +148,13 @@ func TestAddAPIConfig(t *testing.T) {
|
||||
|
||||
func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
apiApp *domain.APIApp
|
||||
resourceOwner string
|
||||
secretGenerator crypto.Generator
|
||||
ctx context.Context
|
||||
apiApp *domain.APIApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
@ -167,9 +169,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "no aggregate id, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -183,8 +183,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "project not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -206,8 +205,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -236,8 +234,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "create api app basic, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -256,12 +253,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
@ -276,8 +268,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
AppName: "app",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
@ -288,7 +279,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
ClientSecretString: "secret",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
@ -297,8 +288,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "create api app jwt, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -317,7 +307,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
nil,
|
||||
"",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
@ -352,10 +342,14 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.AddAPIApplication(tt.args.ctx, tt.args.apiApp, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
got, err := r.AddAPIApplication(tt.args.ctx, tt.args.apiApp, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -371,7 +365,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
|
||||
|
||||
func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -391,9 +385,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "missing appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -414,9 +406,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "missing aggregateid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -437,8 +427,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -460,8 +449,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "no changes, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
@ -475,7 +463,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
nil,
|
||||
"",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
@ -500,8 +488,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
{
|
||||
name: "change api app, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
@ -515,12 +502,7 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
@ -563,7 +545,11 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.ChangeAPIApplication(tt.args.ctx, tt.args.apiApp, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
@ -581,14 +567,13 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
|
||||
|
||||
func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
secretGenerator crypto.Generator
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.APIApp
|
||||
@ -603,9 +588,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "no projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -619,9 +602,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "no appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -636,8 +617,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -654,8 +634,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "change secret, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
@ -669,12 +648,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
@ -682,22 +656,16 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
project.NewAPIConfigSecretChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.APIApp{
|
||||
@ -708,7 +676,7 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
ClientSecretString: "secret",
|
||||
AuthMethodType: domain.APIAuthMethodTypeBasic,
|
||||
State: domain.AppStateActive,
|
||||
},
|
||||
@ -718,9 +686,13 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.ChangeAPIApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
got, err := r.ChangeAPIApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -745,3 +717,105 @@ func newAPIAppChangedEvent(ctx context.Context, appID, projectID, resourceOwner
|
||||
)
|
||||
return event
|
||||
}
|
||||
|
||||
func TestCommands_VerifyAPIClientSecret(t *testing.T) {
|
||||
hasher := &crypto.Hasher{
|
||||
Swapper: passwap.NewSwapper(bcrypt.New(bcrypt.MinCost)),
|
||||
}
|
||||
hashedSecret, err := hasher.Hash("secret")
|
||||
require.NoError(t, err)
|
||||
agg := project.NewAggregate("projectID", "orgID")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
secret string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "filter error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
),
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "app not exists",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-DFnbf", "Errors.Project.App.NotExisting"),
|
||||
},
|
||||
{
|
||||
name: "wrong app type",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowInvalidArgument(nil, "COMMAND-Bf3fw", "Errors.Project.App.IsNotAPI"),
|
||||
},
|
||||
{
|
||||
name: "no secret set",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(), &agg.Aggregate, "appID", "clientID", "", domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-D3t5g", "Errors.Project.App.APIConfigInvalid"),
|
||||
},
|
||||
{
|
||||
name: "check succeeded",
|
||||
secret: "secret",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(), &agg.Aggregate, "appID", "clientID", hashedSecret, domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
project.NewAPIConfigSecretCheckSucceededEvent(context.Background(), &agg.Aggregate, "appID"),
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "check failed",
|
||||
secret: "wrong!",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewAPIConfigAddedEvent(context.Background(), &agg.Aggregate, "appID", "clientID", hashedSecret, domain.APIAuthMethodTypePrivateKeyJWT),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
project.NewAPIConfigSecretCheckFailedEvent(context.Background(), &agg.Aggregate, "appID"),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowInvalidArgument(err, "COMMAND-SADfg", "Errors.Project.App.ClientSecretInvalid"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.eventstore(t),
|
||||
secretHasher: hasher,
|
||||
}
|
||||
err := c.VerifyAPIClientSecret(context.Background(), "projectID", "appID", tt.secret)
|
||||
c.jobs.Wait()
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
@ -117,12 +116,7 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
@ -163,12 +157,7 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypeBasic),
|
||||
),
|
||||
),
|
||||
|
@ -5,11 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
project_repo "github.com/zitadel/zitadel/internal/repository/project"
|
||||
@ -36,12 +33,12 @@ type addOIDCApp struct {
|
||||
SkipSuccessPageForNativeApp bool
|
||||
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ClientSecret string
|
||||
ClientSecretPlain string
|
||||
}
|
||||
|
||||
// AddOIDCAppCommand prepares the commands to add an oidc app. The ClientID will be set during the CreateCommands
|
||||
func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.HashAlgorithm) preparation.Validation {
|
||||
func (c *Commands) AddOIDCAppCommand(app *addOIDCApp) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if app.ID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "PROJE-NnavI", "Errors.Invalid.Argument")
|
||||
@ -77,11 +74,10 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has
|
||||
}
|
||||
|
||||
if app.AuthMethodType == domain.OIDCAuthMethodTypeBasic || app.AuthMethodType == domain.OIDCAuthMethodTypePost {
|
||||
code, err := c.newAppClientSecret(ctx, filter, clientSecretAlg)
|
||||
app.ClientSecret, app.ClientSecretPlain, err = c.newHashedSecret(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.ClientSecret, app.ClientSecretPlain = code.Crypted, code.Plain
|
||||
}
|
||||
|
||||
return []eventstore.Command{
|
||||
@ -118,7 +114,7 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) AddOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
func (c *Commands) AddOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner, appID string) (_ *domain.OIDCApp, err error) {
|
||||
existingApp, err := c.getOIDCAppWriteModel(ctx, oidcApp.AggregateID, appID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -132,10 +128,10 @@ func (c *Commands) AddOIDCApplicationWithID(ctx context.Context, oidcApp *domain
|
||||
return nil, zerrors.ThrowPreconditionFailed(err, "PROJECT-3m9s2", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) AddOIDCApplication(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
func (c *Commands) AddOIDCApplication(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string) (_ *domain.OIDCApp, err error) {
|
||||
if oidcApp == nil || oidcApp.AggregateID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-34Fm0", "Errors.Project.App.Invalid")
|
||||
}
|
||||
@ -153,11 +149,10 @@ func (c *Commands) AddOIDCApplication(ctx context.Context, oidcApp *domain.OIDCA
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
|
||||
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
|
||||
|
||||
func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, project *domain.Project, appID string) (_ *domain.OIDCApp, err error) {
|
||||
addedApplication := NewOIDCApplicationWriteModel(oidcApp.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
|
||||
@ -167,12 +162,14 @@ func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain
|
||||
project_repo.NewApplicationAddedEvent(ctx, projectAgg, oidcApp.AppID, oidcApp.AppName),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
var plain string
|
||||
err = domain.SetNewClientID(oidcApp, c.idGenerator, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, appSecretGenerator)
|
||||
plain, err = domain.SetNewClientSecretIfNeeded(oidcApp, func() (string, string, error) {
|
||||
return c.newHashedSecret(ctx, c.eventstore.Filter) //nolint:staticcheck
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -181,7 +178,7 @@ func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain
|
||||
oidcApp.OIDCVersion,
|
||||
oidcApp.AppID,
|
||||
oidcApp.ClientID,
|
||||
oidcApp.ClientSecret,
|
||||
oidcApp.EncodedHash,
|
||||
trimStringSliceWhiteSpaces(oidcApp.RedirectUris),
|
||||
oidcApp.ResponseTypes,
|
||||
oidcApp.GrantTypes,
|
||||
@ -208,7 +205,7 @@ func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain
|
||||
return nil, err
|
||||
}
|
||||
result := oidcWriteModelToOIDCConfig(addedApplication)
|
||||
result.ClientSecretString = stringPw
|
||||
result.ClientSecretString = plain
|
||||
result.FillCompliance()
|
||||
return result, nil
|
||||
}
|
||||
@ -270,7 +267,7 @@ func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCA
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string, appSecretGenerator crypto.Generator) (*domain.OIDCApp, error) {
|
||||
func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.OIDCApp, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
}
|
||||
@ -285,14 +282,14 @@ func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, a
|
||||
if !existingOIDC.IsOIDC() {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Ghrh3", "Errors.Project.App.IsNotOIDC")
|
||||
}
|
||||
cryptoSecret, stringPW, err := domain.NewClientSecret(appSecretGenerator)
|
||||
encodedHash, plain, err := c.newHashedSecret(ctx, c.eventstore.Filter) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingOIDC.WriteModel)
|
||||
|
||||
pushedEvents, err := c.eventstore.Push(ctx, project_repo.NewOIDCConfigSecretChangedEvent(ctx, projectAgg, appID, cryptoSecret))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, project_repo.NewOIDCConfigSecretChangedEvent(ctx, projectAgg, appID, encodedHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -302,7 +299,7 @@ func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, a
|
||||
}
|
||||
|
||||
result := oidcWriteModelToOIDCConfig(existingOIDC)
|
||||
result.ClientSecretString = stringPW
|
||||
result.ClientSecretString = plain
|
||||
return result, err
|
||||
}
|
||||
|
||||
@ -315,26 +312,35 @@ func (c *Commands) VerifyOIDCClientSecret(ctx context.Context, projectID, appID,
|
||||
return err
|
||||
}
|
||||
if !app.State.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.NotExisting")
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-D8hba", "Errors.Project.App.NotExisting")
|
||||
}
|
||||
if !app.IsOIDC() {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-BHgn2", "Errors.Project.App.IsNotOIDC")
|
||||
}
|
||||
if app.ClientSecret == nil {
|
||||
if app.HashedSecret == "" {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.OIDCConfigInvalid")
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&app.WriteModel)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
err = crypto.CompareHash(app.ClientSecret, []byte(secret), c.codeAlg)
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "passwap.Verify")
|
||||
updated, err := c.secretHasher.Verify(app.HashedSecret, secret)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err == nil {
|
||||
_, err = c.eventstore.Push(ctx, project_repo.NewOIDCConfigSecretCheckSucceededEvent(ctx, projectAgg, app.AppID))
|
||||
return err
|
||||
c.oidcSecretCheckSucceeded(ctx, projectAgg, appID, updated)
|
||||
return nil
|
||||
}
|
||||
_, err = c.eventstore.Push(ctx, project_repo.NewOIDCConfigSecretCheckFailedEvent(ctx, projectAgg, app.AppID))
|
||||
logging.OnError(err).Error("could not push event OIDCClientSecretCheckFailed")
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-Bz542", "Errors.Project.App.ClientSecretInvalid")
|
||||
c.oidcSecretCheckFailed(ctx, projectAgg, appID)
|
||||
return zerrors.ThrowInvalidArgument(err, "COMMAND-Bz542", "Errors.Project.App.ClientSecretInvalid")
|
||||
}
|
||||
|
||||
func (c *Commands) OIDCSecretCheckSucceeded(ctx context.Context, appID, projectID, resourceOwner, updated string) {
|
||||
agg := project_repo.NewAggregate(projectID, resourceOwner)
|
||||
c.oidcSecretCheckSucceeded(ctx, &agg.Aggregate, appID, updated)
|
||||
}
|
||||
|
||||
func (c *Commands) OIDCSecretCheckFailed(ctx context.Context, appID, projectID, resourceOwner string) {
|
||||
agg := project_repo.NewAggregate(projectID, resourceOwner)
|
||||
c.oidcSecretCheckFailed(ctx, &agg.Aggregate, appID)
|
||||
}
|
||||
|
||||
func (c *Commands) getOIDCAppWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*OIDCApplicationWriteModel, error) {
|
||||
@ -366,3 +372,18 @@ func trimStringSliceWhiteSpaces(slice []string) []string {
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func (c *Commands) oidcSecretCheckSucceeded(ctx context.Context, agg *eventstore.Aggregate, appID, updated string) {
|
||||
cmds := append(
|
||||
make([]eventstore.Command, 0, 2),
|
||||
project_repo.NewOIDCConfigSecretCheckSucceededEvent(ctx, agg, appID),
|
||||
)
|
||||
if updated != "" {
|
||||
cmds = append(cmds, project_repo.NewOIDCConfigSecretHashUpdatedEvent(ctx, agg, appID, updated))
|
||||
}
|
||||
c.asyncPush(ctx, cmds...)
|
||||
}
|
||||
|
||||
func (c *Commands) oidcSecretCheckFailed(ctx context.Context, agg *eventstore.Aggregate, appID string) {
|
||||
c.asyncPush(ctx, project_repo.NewOIDCConfigSecretCheckFailedEvent(ctx, agg, appID))
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ type OIDCApplicationWriteModel struct {
|
||||
AppID string
|
||||
AppName string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
HashedSecret string
|
||||
ClientSecretString string
|
||||
RedirectUris []string
|
||||
ResponseTypes []domain.OIDCResponseType
|
||||
@ -100,6 +100,11 @@ func (wm *OIDCApplicationWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.OIDCConfigSecretHashUpdatedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
@ -131,7 +136,9 @@ func (wm *OIDCApplicationWriteModel) Reduce() error {
|
||||
case *project.OIDCConfigChangedEvent:
|
||||
wm.appendChangeOIDCEvent(e)
|
||||
case *project.OIDCConfigSecretChangedEvent:
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.HashedSecret = crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)
|
||||
case *project.OIDCConfigSecretHashUpdatedEvent:
|
||||
wm.HashedSecret = e.HashedSecret
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
}
|
||||
@ -142,7 +149,7 @@ func (wm *OIDCApplicationWriteModel) Reduce() error {
|
||||
func (wm *OIDCApplicationWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAddedEvent) {
|
||||
wm.oidc = true
|
||||
wm.ClientID = e.ClientID
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.HashedSecret = crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)
|
||||
wm.RedirectUris = e.RedirectUris
|
||||
wm.ResponseTypes = e.ResponseTypes
|
||||
wm.GrantTypes = e.GrantTypes
|
||||
@ -223,8 +230,9 @@ func (wm *OIDCApplicationWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
project.OIDCConfigAddedType,
|
||||
project.OIDCConfigChangedType,
|
||||
project.OIDCConfigSecretChangedType,
|
||||
project.ProjectRemovedType).
|
||||
Builder()
|
||||
project.OIDCConfigSecretHashUpdatedType,
|
||||
project.ProjectRemovedType,
|
||||
).Builder()
|
||||
}
|
||||
|
||||
func (wm *OIDCApplicationWriteModel) NewChangedEvent(
|
||||
|
@ -2,10 +2,14 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/passwap"
|
||||
"github.com/zitadel/passwap/bcrypt"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
@ -23,9 +27,8 @@ func TestAddOIDCApp(t *testing.T) {
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
app *addOIDCApp
|
||||
clientSecretAlg crypto.HashAlgorithm
|
||||
filter preparation.FilterToQueryReducer
|
||||
app *addOIDCApp
|
||||
filter preparation.FilterToQueryReducer
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@ -156,7 +159,7 @@ func TestAddOIDCApp(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"id",
|
||||
"clientID@project",
|
||||
nil,
|
||||
"",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -221,7 +224,7 @@ func TestAddOIDCApp(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"id",
|
||||
"clientID@project",
|
||||
nil,
|
||||
"",
|
||||
nil,
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -240,17 +243,85 @@ func TestAddOIDCApp(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with secret",
|
||||
fields: fields{
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "clientID"),
|
||||
},
|
||||
args: args{
|
||||
app: &addOIDCApp{
|
||||
AddApp: AddApp{
|
||||
Aggregate: *agg,
|
||||
ID: "id",
|
||||
Name: "name",
|
||||
},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
Version: domain.OIDCVersionV1,
|
||||
|
||||
ApplicationType: domain.OIDCApplicationTypeWeb,
|
||||
AuthMethodType: domain.OIDCAuthMethodTypeBasic,
|
||||
AccessTokenType: domain.OIDCTokenTypeBearer,
|
||||
},
|
||||
filter: NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
project.NewProjectAddedEvent(
|
||||
ctx,
|
||||
&agg.Aggregate,
|
||||
"project",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
domain.PrivateLabelingSettingUnspecified,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
project.NewApplicationAddedEvent(ctx, &agg.Aggregate,
|
||||
"id",
|
||||
"name",
|
||||
),
|
||||
project.NewOIDCConfigAddedEvent(ctx, &agg.Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"id",
|
||||
"clientID@project",
|
||||
"secret",
|
||||
nil,
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypeBasic,
|
||||
nil,
|
||||
false,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
nil,
|
||||
false,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := Commands{
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
AssertValidation(t,
|
||||
context.Background(),
|
||||
c.AddOIDCAppCommand(
|
||||
tt.args.app,
|
||||
tt.args.clientSecretAlg,
|
||||
), tt.args.filter, tt.want)
|
||||
})
|
||||
}
|
||||
@ -258,14 +329,13 @@ func TestAddOIDCApp(t *testing.T) {
|
||||
|
||||
func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
oidcApp *domain.OIDCApp
|
||||
resourceOwner string
|
||||
secretGenerator crypto.Generator
|
||||
ctx context.Context
|
||||
oidcApp *domain.OIDCApp
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OIDCApp
|
||||
@ -280,9 +350,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
{
|
||||
name: "no aggregate id, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -296,8 +364,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
{
|
||||
name: "project not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -319,8 +386,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
{
|
||||
name: "invalid app, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -349,8 +415,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
{
|
||||
name: "create oidc app basic using whitespaces in uris, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -370,12 +435,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -418,8 +478,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
AdditionalOrigins: []string{" https://sub.test.ch "},
|
||||
SkipNativeAppSuccessPage: true,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
@ -430,7 +489,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
ClientSecretString: "secret",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
@ -454,8 +513,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
{
|
||||
name: "create oidc app basic, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewProjectAddedEvent(context.Background(),
|
||||
@ -475,12 +533,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -523,8 +576,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
AdditionalOrigins: []string{"https://sub.test.ch"},
|
||||
SkipNativeAppSuccessPage: true,
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
@ -535,7 +587,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
ClientSecretString: "secret",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
@ -560,10 +612,14 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.AddOIDCApplication(tt.args.ctx, tt.args.oidcApp, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
got, err := r.AddOIDCApplication(tt.args.ctx, tt.args.oidcApp, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -709,12 +765,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -783,12 +834,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -857,12 +903,7 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -965,14 +1006,13 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
|
||||
|
||||
func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
secretGenerator crypto.Generator
|
||||
ctx context.Context
|
||||
appID string
|
||||
projectID string
|
||||
resourceOwner string
|
||||
}
|
||||
type res struct {
|
||||
want *domain.OIDCApp
|
||||
@ -987,9 +1027,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "no projectid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1003,9 +1041,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "no appid, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1020,8 +1056,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "app not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -1038,8 +1073,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
{
|
||||
name: "change secret, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(),
|
||||
@ -1054,12 +1088,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
domain.OIDCVersionV1,
|
||||
"app1",
|
||||
"client1@project",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -1081,22 +1110,16 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
project.NewOIDCConfigSecretChangedEvent(context.Background(),
|
||||
&project.NewAggregate("project1", "org1").Aggregate,
|
||||
"app1",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
ctx: context.Background(),
|
||||
projectID: "project1",
|
||||
appID: "app1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.OIDCApp{
|
||||
@ -1107,7 +1130,7 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
AppID: "app1",
|
||||
AppName: "app",
|
||||
ClientID: "client1@project",
|
||||
ClientSecretString: "a",
|
||||
ClientSecretString: "secret",
|
||||
AuthMethodType: domain.OIDCAuthMethodTypePost,
|
||||
OIDCVersion: domain.OIDCVersionV1,
|
||||
RedirectUris: []string{"https://test.ch"},
|
||||
@ -1129,11 +1152,15 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Run(tt.name, func(*testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.ChangeOIDCApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
got, err := r.ChangeOIDCApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1165,3 +1192,165 @@ func newOIDCAppChangedEvent(ctx context.Context, appID, projectID, resourceOwner
|
||||
)
|
||||
return event
|
||||
}
|
||||
|
||||
func TestCommands_VerifyOIDCClientSecret(t *testing.T) {
|
||||
hasher := &crypto.Hasher{
|
||||
Swapper: passwap.NewSwapper(bcrypt.New(bcrypt.MinCost)),
|
||||
}
|
||||
hashedSecret, err := hasher.Hash("secret")
|
||||
require.NoError(t, err)
|
||||
agg := project.NewAggregate("projectID", "orgID")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
secret string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "filter error",
|
||||
eventstore: expectEventstore(
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
),
|
||||
wantErr: io.ErrClosedPipe,
|
||||
},
|
||||
{
|
||||
name: "app not exists",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-D8hba", "Errors.Project.App.NotExisting"),
|
||||
},
|
||||
{
|
||||
name: "wrong app type",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowInvalidArgument(nil, "COMMAND-BHgn2", "Errors.Project.App.IsNotOIDC"),
|
||||
},
|
||||
{
|
||||
name: "no secret set",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&agg.Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"appID",
|
||||
"client1@project",
|
||||
"",
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1,
|
||||
[]string{"https://sub.test.ch"},
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.OIDCConfigInvalid"),
|
||||
},
|
||||
{
|
||||
name: "check succeeded",
|
||||
secret: "secret",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&agg.Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"appID",
|
||||
"client1@project",
|
||||
hashedSecret,
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1,
|
||||
[]string{"https://sub.test.ch"},
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
project.NewOIDCConfigSecretCheckSucceededEvent(context.Background(), &agg.Aggregate, "appID"),
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "check failed",
|
||||
secret: "wrong!",
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
project.NewApplicationAddedEvent(context.Background(), &agg.Aggregate, "appID", "appName"),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
project.NewOIDCConfigAddedEvent(context.Background(),
|
||||
&agg.Aggregate,
|
||||
domain.OIDCVersionV1,
|
||||
"appID",
|
||||
"client1@project",
|
||||
hashedSecret,
|
||||
[]string{"https://test.ch"},
|
||||
[]domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
[]domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
domain.OIDCApplicationTypeWeb,
|
||||
domain.OIDCAuthMethodTypePost,
|
||||
[]string{"https://test.ch/logout"},
|
||||
true,
|
||||
domain.OIDCTokenTypeBearer,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
time.Second*1,
|
||||
[]string{"https://sub.test.ch"},
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
project.NewOIDCConfigSecretCheckFailedEvent(context.Background(), &agg.Aggregate, "appID"),
|
||||
),
|
||||
),
|
||||
wantErr: zerrors.ThrowInvalidArgument(err, "COMMAND-Bz542", "Errors.Project.App.ClientSecretInvalid"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.eventstore(t),
|
||||
secretHasher: hasher,
|
||||
}
|
||||
err := c.VerifyOIDCClientSecret(context.Background(), "projectID", "appID", tt.secret)
|
||||
c.jobs.Wait()
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ type SessionCommands struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventCommands []eventstore.Command
|
||||
|
||||
hasher *crypto.PasswordHasher
|
||||
hasher *crypto.Hasher
|
||||
intentAlg crypto.EncryptionAlgorithm
|
||||
totpAlg crypto.EncryptionAlgorithm
|
||||
otpAlg crypto.EncryptionAlgorithm
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
createToken func(sessionID string) (id string, token string, err error)
|
||||
now func() time.Time
|
||||
}
|
||||
@ -49,7 +49,7 @@ func (c *Commands) NewSessionCommands(cmds []SessionCommand, session *SessionWri
|
||||
intentAlg: c.idpConfigEncryption,
|
||||
totpAlg: c.multifactors.OTP.CryptoMFA,
|
||||
otpAlg: c.userEncryption,
|
||||
createCode: c.newCodeWithDefault,
|
||||
createCode: c.newEncryptedCodeWithDefault,
|
||||
createToken: c.sessionTokenCreator,
|
||||
now: time.Now,
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func CheckOTPSMS(code string) SessionCommand {
|
||||
if challenge == nil {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-SF3tv", "Errors.User.Code.NotFound")
|
||||
}
|
||||
err = crypto.VerifyCodeWithAlgorithm(challenge.CreationDate, challenge.Expiry, challenge.Code, code, cmd.otpAlg)
|
||||
err = crypto.VerifyCode(challenge.CreationDate, challenge.Expiry, challenge.Code, code, cmd.otpAlg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -138,7 +138,7 @@ func CheckOTPEmail(code string) SessionCommand {
|
||||
if challenge == nil {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-zF3g3", "Errors.User.Code.NotFound")
|
||||
}
|
||||
err = crypto.VerifyCodeWithAlgorithm(challenge.CreationDate, challenge.Expiry, challenge.Code, code, cmd.otpAlg)
|
||||
err = crypto.VerifyCode(challenge.CreationDate, challenge.Expiry, challenge.Code, code, cmd.otpAlg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func TestCommands_CreateOTPSMSChallengeReturnCode(t *testing.T) {
|
||||
type fields struct {
|
||||
userID string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type res struct {
|
||||
err error
|
||||
@ -65,7 +65,7 @@ func TestCommands_CreateOTPSMSChallengeReturnCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
createCode: mockCodeWithDefault("1234567", 5*time.Minute),
|
||||
createCode: mockEncryptedCodeWithDefault("1234567", 5*time.Minute),
|
||||
},
|
||||
res: res{
|
||||
returnCode: "1234567",
|
||||
@ -122,7 +122,7 @@ func TestCommands_CreateOTPSMSChallenge(t *testing.T) {
|
||||
type fields struct {
|
||||
userID string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type res struct {
|
||||
err error
|
||||
@ -166,7 +166,7 @@ func TestCommands_CreateOTPSMSChallenge(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
createCode: mockCodeWithDefault("1234567", 5*time.Minute),
|
||||
createCode: mockEncryptedCodeWithDefault("1234567", 5*time.Minute),
|
||||
},
|
||||
res: res{
|
||||
commands: []eventstore.Command{
|
||||
@ -292,7 +292,7 @@ func TestCommands_CreateOTPEmailChallengeURLTemplate(t *testing.T) {
|
||||
type fields struct {
|
||||
userID string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type args struct {
|
||||
urlTmpl string
|
||||
@ -361,7 +361,7 @@ func TestCommands_CreateOTPEmailChallengeURLTemplate(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
createCode: mockCodeWithDefault("1234567", 5*time.Minute),
|
||||
createCode: mockEncryptedCodeWithDefault("1234567", 5*time.Minute),
|
||||
},
|
||||
res: res{
|
||||
commands: []eventstore.Command{
|
||||
@ -421,7 +421,7 @@ func TestCommands_CreateOTPEmailChallengeReturnCode(t *testing.T) {
|
||||
type fields struct {
|
||||
userID string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type res struct {
|
||||
err error
|
||||
@ -465,7 +465,7 @@ func TestCommands_CreateOTPEmailChallengeReturnCode(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
createCode: mockCodeWithDefault("1234567", 5*time.Minute),
|
||||
createCode: mockEncryptedCodeWithDefault("1234567", 5*time.Minute),
|
||||
},
|
||||
res: res{
|
||||
returnCode: "1234567",
|
||||
@ -523,7 +523,7 @@ func TestCommands_CreateOTPEmailChallenge(t *testing.T) {
|
||||
type fields struct {
|
||||
userID string
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
createCode cryptoCodeWithDefaultFunc
|
||||
createCode encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type res struct {
|
||||
err error
|
||||
@ -566,7 +566,7 @@ func TestCommands_CreateOTPEmailChallenge(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
createCode: mockCodeWithDefault("1234567", 5*time.Minute),
|
||||
createCode: mockEncryptedCodeWithDefault("1234567", 5*time.Minute),
|
||||
},
|
||||
res: res{
|
||||
commands: []eventstore.Command{
|
||||
|
@ -476,8 +476,8 @@ func ExistsUser(ctx context.Context, filter preparation.FilterToQueryReducer, id
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func (c *Commands) newUserInitCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCode, error) {
|
||||
return c.newCode(ctx, filter, domain.SecretGeneratorTypeInitCode, alg)
|
||||
func (c *Commands) newUserInitCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypeInitCode, alg)
|
||||
}
|
||||
|
||||
func userWriteModelByID(ctx context.Context, filter preparation.FilterToQueryReducer, userID, resourceOwner string) (*UserWriteModel, error) {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
@ -77,7 +78,7 @@ type AddLink struct {
|
||||
IDPExternalID string
|
||||
}
|
||||
|
||||
func (h *AddHuman) Validate(hasher *crypto.PasswordHasher) (err error) {
|
||||
func (h *AddHuman) Validate(hasher *crypto.Hasher) (err error) {
|
||||
if err := h.Email.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -164,7 +165,7 @@ type humanCreationCommand interface {
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func (c *Commands) AddHumanCommand(human *AddHuman, orgID string, hasher *crypto.PasswordHasher, codeAlg crypto.EncryptionAlgorithm, allowInitMail bool) preparation.Validation {
|
||||
func (c *Commands) AddHumanCommand(human *AddHuman, orgID string, hasher *crypto.Hasher, codeAlg crypto.EncryptionAlgorithm, allowInitMail bool) preparation.Validation {
|
||||
return func() (_ preparation.CreateCommands, err error) {
|
||||
if err := human.Validate(hasher); err != nil {
|
||||
return nil, err
|
||||
@ -329,17 +330,19 @@ func (c *Commands) addHumanCommandCheckID(ctx context.Context, filter preparatio
|
||||
return nil
|
||||
}
|
||||
|
||||
func addHumanCommandPassword(ctx context.Context, filter preparation.FilterToQueryReducer, createCmd humanCreationCommand, human *AddHuman, hasher *crypto.PasswordHasher) (err error) {
|
||||
func addHumanCommandPassword(ctx context.Context, filter preparation.FilterToQueryReducer, createCmd humanCreationCommand, human *AddHuman, hasher *crypto.Hasher) (err error) {
|
||||
if human.Password != "" {
|
||||
if err = humanValidatePassword(ctx, filter, human.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
secret, err := hasher.Hash(human.Password)
|
||||
_, spanHash := tracing.NewNamedSpan(ctx, "passwap.Hash")
|
||||
encodedHash, err := hasher.Hash(human.Password)
|
||||
spanHash.EndWithError(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
createCmd.AddPasswordData(secret, human.PasswordChangeRequired)
|
||||
createCmd.AddPasswordData(encodedHash, human.PasswordChangeRequired)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -589,7 +592,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
|
||||
|
||||
human.EnsureDisplayName()
|
||||
if human.Password != nil {
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordHasher, human.Password.ChangeRequired); err != nil {
|
||||
if err := human.HashPasswordIfExisting(ctx, pwPolicy, c.userPasswordHasher, human.Password.ChangeRequired); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, emailCodeGenerator)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, emailCodeGenerator.Alg())
|
||||
if err == nil {
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailVerifiedEvent(ctx, userAgg))
|
||||
if err != nil {
|
||||
|
@ -67,7 +67,7 @@ func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwne
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, initCodeGenerator)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, initCodeGenerator.Alg())
|
||||
if err != nil {
|
||||
_, err = c.eventstore.Push(ctx, user.NewHumanInitializedCheckFailedEvent(ctx, userAgg))
|
||||
logging.WithFields("userID", userAgg.ID).OnError(err).Error("NewHumanInitializedCheckFailedEvent push failed")
|
||||
|
@ -292,7 +292,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
|
||||
func TestCommandSide_VerifyInitCode(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -455,7 +455,7 @@ func (c *Commands) sendHumanOTP(
|
||||
if !existingOTP.OTPAdded() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-SFD52", "Errors.User.MFA.OTP.NotReady")
|
||||
}
|
||||
config, err := secretGeneratorConfigWithDefault(ctx, c.eventstore.Filter, secretGeneratorType, defaultSecretGenerator)
|
||||
config, err := cryptoGeneratorConfigWithDefault(ctx, c.eventstore.Filter, secretGeneratorType, defaultSecretGenerator) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -515,7 +515,7 @@ func (c *Commands) humanCheckOTP(
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-S34gh", "Errors.User.Code.NotFound")
|
||||
}
|
||||
userAgg := &user.NewAggregate(userID, existingOTP.ResourceOwner()).Aggregate
|
||||
err = crypto.VerifyCodeWithAlgorithm(existingOTP.CodeCreationDate(), existingOTP.CodeExpiry(), existingOTP.Code(), code, c.userEncryption)
|
||||
err = crypto.VerifyCode(existingOTP.CodeCreationDate(), existingOTP.CodeExpiry(), existingOTP.Code(), code, c.userEncryption)
|
||||
if err == nil {
|
||||
_, err = c.eventstore.Push(ctx, checkSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return err
|
||||
|
@ -53,7 +53,7 @@ func (c *Commands) SetPasswordWithVerifyCode(ctx context.Context, orgID, userID,
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
|
||||
}
|
||||
|
||||
err = crypto.VerifyCodeWithAlgorithm(wm.CodeCreationDate, wm.CodeExpiry, wm.Code, code, c.userEncryption)
|
||||
err = crypto.VerifyCode(wm.CodeCreationDate, wm.CodeExpiry, wm.Code, code, c.userEncryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.EncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.EncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.EncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.EncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanInitialCodeAddedEvent:
|
||||
@ -48,7 +48,7 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
case *user.HumanInitializedCheckSucceededEvent:
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.EncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.EncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.Code = nil
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
func TestCommandSide_SetOneTimePassword(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
@ -270,7 +270,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -598,7 +598,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) {
|
||||
|
||||
func TestCommandSide_ChangePassword(t *testing.T) {
|
||||
type fields struct {
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -1202,7 +1202,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) {
|
||||
func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -79,7 +79,7 @@ func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceo
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, phoneCodeGenerator)
|
||||
err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, phoneCodeGenerator.Alg())
|
||||
if err == nil {
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPhoneVerifiedEvent(ctx, userAgg))
|
||||
if err != nil {
|
||||
@ -115,7 +115,7 @@ func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID,
|
||||
if existingPhone.IsPhoneVerified {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
|
||||
}
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode)
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
codeAlg crypto.EncryptionAlgorithm
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -245,7 +245,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -312,7 +312,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -380,7 +380,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -450,7 +450,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -521,7 +521,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -593,7 +593,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -996,7 +996,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("phonecode", time.Hour),
|
||||
newCode: mockEncryptedCode("phonecode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1061,7 +1061,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1136,7 +1136,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("phoneCode", time.Hour),
|
||||
newCode: mockEncryptedCode("phoneCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1204,7 +1204,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1242,7 +1242,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||
userEncryption: tt.fields.codeAlg,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newCode: tt.fields.newCode,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
}
|
||||
err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail)
|
||||
if tt.res.err == nil {
|
||||
@ -1266,7 +1266,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -2483,7 +2483,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
userPasswordHasher *crypto.Hasher
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -4328,7 +4328,7 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
type args struct {
|
||||
human *AddHuman
|
||||
orgID string
|
||||
hasher *crypto.PasswordHasher
|
||||
hasher *crypto.Hasher
|
||||
filter preparation.FilterToQueryReducer
|
||||
codeAlg crypto.EncryptionAlgorithm
|
||||
allowInitMail bool
|
||||
|
@ -575,7 +575,7 @@ func (c *Commands) humanVerifyPasswordlessInitCode(ctx context.Context, userID,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = crypto.VerifyCode(initCode.ChangeDate, initCode.Expiration, initCode.CryptoCode, verificationCode, passwordlessCodeGenerator)
|
||||
err = crypto.VerifyCode(initCode.ChangeDate, initCode.Expiration, initCode.CryptoCode, verificationCode, passwordlessCodeGenerator.Alg())
|
||||
if err != nil || initCode.State != domain.PasswordlessInitCodeStateActive {
|
||||
userAgg := UserAggregateFromWriteModel(&initCode.WriteModel)
|
||||
_, err = c.eventstore.Push(ctx, usr_repo.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, codeID))
|
||||
|
@ -18,8 +18,7 @@ type MachineWriteModel struct {
|
||||
Description string
|
||||
UserState domain.UserState
|
||||
AccessTokenType domain.OIDCTokenType
|
||||
|
||||
ClientSecret *crypto.CryptoValue
|
||||
HashedSecret string
|
||||
}
|
||||
|
||||
func NewMachineWriteModel(userID, resourceOwner string) *MachineWriteModel {
|
||||
@ -71,9 +70,11 @@ func (wm *MachineWriteModel) Reduce() error {
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
case *user.MachineSecretSetEvent:
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.HashedSecret = crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)
|
||||
case *user.MachineSecretRemovedEvent:
|
||||
wm.ClientSecret = nil
|
||||
wm.HashedSecret = ""
|
||||
case *user.MachineSecretHashUpdatedEvent:
|
||||
wm.HashedSecret = e.HashedSecret
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
@ -94,8 +95,9 @@ func (wm *MachineWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
user.UserReactivatedType,
|
||||
user.UserRemovedType,
|
||||
user.MachineSecretSetType,
|
||||
user.MachineSecretRemovedType).
|
||||
Builder()
|
||||
user.MachineSecretRemovedType,
|
||||
user.MachineSecretHashUpdatedType,
|
||||
).Builder()
|
||||
}
|
||||
|
||||
func (wm *MachineWriteModel) NewChangedEvent(
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/command/preparation"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
@ -15,9 +14,9 @@ type GenerateMachineSecret struct {
|
||||
ClientSecret string
|
||||
}
|
||||
|
||||
func (c *Commands) GenerateMachineSecret(ctx context.Context, userID string, resourceOwner string, generator crypto.Generator, set *GenerateMachineSecret) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) GenerateMachineSecret(ctx context.Context, userID string, resourceOwner string, set *GenerateMachineSecret) (*domain.ObjectDetails, error) {
|
||||
agg := user.NewAggregate(userID, resourceOwner)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareGenerateMachineSecret(agg, generator, set))
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareGenerateMachineSecret(agg, set)) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -34,7 +33,7 @@ func (c *Commands) GenerateMachineSecret(ctx context.Context, userID string, res
|
||||
}, nil
|
||||
}
|
||||
|
||||
func prepareGenerateMachineSecret(a *user.Aggregate, generator crypto.Generator, set *GenerateMachineSecret) preparation.Validation {
|
||||
func (c *Commands) prepareGenerateMachineSecret(a *user.Aggregate, set *GenerateMachineSecret) preparation.Validation {
|
||||
return func() (_ preparation.CreateCommands, err error) {
|
||||
if a.ResourceOwner == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-x0992n", "Errors.ResourceOwnerMissing")
|
||||
@ -50,15 +49,14 @@ func prepareGenerateMachineSecret(a *user.Aggregate, generator crypto.Generator,
|
||||
if !isUserStateExists(writeModel.UserState) {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-x8910n", "Errors.User.NotExisting")
|
||||
}
|
||||
|
||||
clientSecret, secretString, err := domain.NewMachineClientSecret(generator)
|
||||
encodedHash, plain, err := c.newHashedSecret(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set.ClientSecret = secretString
|
||||
set.ClientSecret = plain
|
||||
|
||||
return []eventstore.Command{
|
||||
user.NewMachineSecretSetEvent(ctx, &a.Aggregate, clientSecret),
|
||||
user.NewMachineSecretSetEvent(ctx, &a.Aggregate, encodedHash),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
@ -99,7 +97,7 @@ func prepareRemoveMachineSecret(a *user.Aggregate) preparation.Validation {
|
||||
if !isUserStateExists(writeModel.UserState) {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-x7s802", "Errors.User.NotExisting")
|
||||
}
|
||||
if writeModel.ClientSecret == nil {
|
||||
if writeModel.HashedSecret == "" {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-coi82n", "Errors.User.Machine.Secret.NotExisting")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
@ -109,9 +107,16 @@ func prepareRemoveMachineSecret(a *user.Aggregate) preparation.Validation {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) MachineSecretCheckSucceeded(ctx context.Context, userID, resourceOwner string) {
|
||||
func (c *Commands) MachineSecretCheckSucceeded(ctx context.Context, userID, resourceOwner, updated string) {
|
||||
agg := user.NewAggregate(userID, resourceOwner)
|
||||
c.asyncPush(ctx, user.NewMachineSecretCheckSucceededEvent(ctx, &agg.Aggregate))
|
||||
cmds := append(
|
||||
make([]eventstore.Command, 0, 2),
|
||||
user.NewMachineSecretCheckSucceededEvent(ctx, &agg.Aggregate),
|
||||
)
|
||||
if updated != "" {
|
||||
cmds = append(cmds, user.NewMachineSecretHashUpdatedEvent(ctx, &agg.Aggregate, updated))
|
||||
}
|
||||
c.asyncPush(ctx, cmds...)
|
||||
}
|
||||
|
||||
func (c *Commands) MachineSecretCheckFailed(ctx context.Context, userID, resourceOwner string) {
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
@ -17,13 +16,12 @@ import (
|
||||
|
||||
func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userID string
|
||||
resourceOwner string
|
||||
generator crypto.Generator
|
||||
set *GenerateMachineSecret
|
||||
}
|
||||
type res struct {
|
||||
@ -40,15 +38,12 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
{
|
||||
name: "user invalid, invalid argument error userID",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "",
|
||||
resourceOwner: "org1",
|
||||
generator: GetMockSecretGenerator(t),
|
||||
set: nil,
|
||||
},
|
||||
res: res{
|
||||
@ -58,15 +53,12 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
{
|
||||
name: "user invalid, invalid argument error resourceowner",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "",
|
||||
generator: GetMockSecretGenerator(t),
|
||||
set: nil,
|
||||
},
|
||||
res: res{
|
||||
@ -76,8 +68,7 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@ -85,7 +76,6 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
generator: GetMockSecretGenerator(t),
|
||||
set: nil,
|
||||
},
|
||||
res: res{
|
||||
@ -95,8 +85,7 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
{
|
||||
name: "add machine secret, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewMachineAddedEvent(context.Background(),
|
||||
@ -112,12 +101,7 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
expectPush(
|
||||
user.NewMachineSecretSetEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -126,7 +110,6 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
generator: GetMockSecretGenerator(t),
|
||||
set: &GenerateMachineSecret{},
|
||||
},
|
||||
res: res{
|
||||
@ -134,7 +117,7 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
secret: &GenerateMachineSecret{
|
||||
ClientSecret: "a",
|
||||
ClientSecret: "secret",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -142,9 +125,13 @@ func TestCommandSide_GenerateMachineSecret(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
newHashedSecret: mockHashedSecret("secret"),
|
||||
defaultSecretGenerators: &SecretGenerators{
|
||||
ClientSecret: emptyConfig,
|
||||
},
|
||||
}
|
||||
got, err := r.GenerateMachineSecret(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.generator, tt.args.set)
|
||||
got, err := r.GenerateMachineSecret(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.set)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -274,12 +261,7 @@ func TestCommandSide_RemoveMachineSecret(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
user.NewMachineSecretSetEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
"secret",
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -333,7 +315,7 @@ func TestCommands_MachineSecretCheckSucceeded(t *testing.T) {
|
||||
expectPushSlow(time.Second/100, cmd),
|
||||
),
|
||||
}
|
||||
c.MachineSecretCheckSucceeded(ctx, "userID", "orgID")
|
||||
c.MachineSecretCheckSucceeded(ctx, "userID", "orgID", "")
|
||||
require.NoError(t, c.Close(ctx))
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ func (c *Commands) ChangeUserEmailVerified(ctx context.Context, userID, email st
|
||||
}
|
||||
|
||||
func (c *Commands) changeUserEmailWithCode(ctx context.Context, userID, email string, alg crypto.EncryptionAlgorithm, returnCode bool, urlTmpl string) (*domain.Email, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode)
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -85,7 +85,7 @@ func (c *Commands) changeUserEmailWithCode(ctx context.Context, userID, email st
|
||||
}
|
||||
|
||||
func (c *Commands) resendUserEmailCode(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm, returnCode bool, urlTmpl string) (*domain.Email, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode) //nolint:staticcheck
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -152,7 +152,7 @@ func (c *Commands) resendUserEmailCodeWithGeneratorEvents(ctx context.Context, u
|
||||
}
|
||||
|
||||
func (c *Commands) VerifyUserEmail(ctx context.Context, userID, code string, alg crypto.EncryptionAlgorithm) (*domain.ObjectDetails, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode)
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -262,7 +262,7 @@ func (c *UserEmailEvents) VerifyCode(ctx context.Context, code string, gen crypt
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-Fia4a", "Errors.User.Code.Empty")
|
||||
}
|
||||
|
||||
err := crypto.VerifyCode(c.model.CodeCreationDate, c.model.CodeExpiry, c.model.Code, code, gen)
|
||||
err := crypto.VerifyCode(c.model.CodeCreationDate, c.model.CodeExpiry, c.model.Code, code, gen.Alg())
|
||||
if err == nil {
|
||||
c.events = append(c.events, user.NewHumanEmailVerifiedEvent(ctx, c.aggregate))
|
||||
return nil
|
||||
|
@ -51,7 +51,7 @@ type Password struct {
|
||||
ChangeRequired bool
|
||||
}
|
||||
|
||||
func (h *ChangeHuman) Validate(hasher *crypto.PasswordHasher) (err error) {
|
||||
func (h *ChangeHuman) Validate(hasher *crypto.Hasher) (err error) {
|
||||
if h.Email != nil && h.Email.Address != "" {
|
||||
if err := h.Email.Validate(); err != nil {
|
||||
return err
|
||||
@ -72,7 +72,7 @@ func (h *ChangeHuman) Validate(hasher *crypto.PasswordHasher) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Password) Validate(hasher *crypto.PasswordHasher) error {
|
||||
func (p *Password) Validate(hasher *crypto.Hasher) error {
|
||||
if p.EncodedPasswordHash != nil {
|
||||
if !hasher.EncodingSupported(*p.EncodedPasswordHash) {
|
||||
return zerrors.ThrowInvalidArgument(nil, "USER-oz74onzvqr", "Errors.User.Password.NotSupported")
|
||||
@ -373,7 +373,7 @@ func (c *Commands) changeUserPassword(ctx context.Context, cmds []eventstore.Com
|
||||
|
||||
// Either have a code to set the password
|
||||
if password.PasswordCode != nil {
|
||||
if err := crypto.VerifyCodeWithAlgorithm(wm.PasswordCodeCreationDate, wm.PasswordCodeExpiry, wm.PasswordCode, *password.PasswordCode, alg); err != nil {
|
||||
if err := crypto.VerifyCode(wm.PasswordCodeCreationDate, wm.PasswordCodeExpiry, wm.PasswordCode, *password.PasswordCode, alg); err != nil {
|
||||
return cmds, err
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
newCode cryptoCodeFunc
|
||||
userPasswordHasher *crypto.Hasher
|
||||
newCode encrypedCodeFunc
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
@ -247,7 +247,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -283,7 +283,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -349,7 +349,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -420,7 +420,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -492,7 +492,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -565,7 +565,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -974,7 +974,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockCode("phonecode", time.Hour),
|
||||
newCode: mockEncryptedCode("phonecode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1040,7 +1040,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1116,7 +1116,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
newCode: mockCode("phoneCode", time.Hour),
|
||||
newCode: mockEncryptedCode("phoneCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1185,7 +1185,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
newCode: mockCode("userinit", time.Hour),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1223,7 +1223,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newCode: tt.fields.newCode,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
err := r.AddUserHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.allowInitMail, tt.args.codeAlg)
|
||||
@ -1247,8 +1247,8 @@ func TestCommandSide_AddUserHuman(t *testing.T) {
|
||||
func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
userPasswordHasher *crypto.PasswordHasher
|
||||
newCode cryptoCodeFunc
|
||||
userPasswordHasher *crypto.Hasher
|
||||
newCode encrypedCodeFunc
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
@ -1562,7 +1562,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1741,7 +1741,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("emailCode", time.Hour),
|
||||
newCode: mockEncryptedCode("emailCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1791,7 +1791,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("phoneCode", time.Hour),
|
||||
newCode: mockEncryptedCode("phoneCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -1939,7 +1939,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("phoneCode", time.Hour),
|
||||
newCode: mockEncryptedCode("phoneCode", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -2546,7 +2546,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||
newCode: tt.fields.newCode,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
err := r.ChangeUserHuman(tt.args.ctx, tt.args.human, tt.args.codeAlg)
|
||||
|
@ -266,7 +266,7 @@ func (wm *UserV2WriteModel) Reduce() error {
|
||||
case *user.HumanPasswordCheckSucceededEvent:
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.PasswordEncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordEncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordChangeRequired = e.ChangeRequired
|
||||
wm.EmptyPasswordCode()
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
@ -470,7 +470,7 @@ func (wm *UserV2WriteModel) reduceHumanAddedEvent(e *user.HumanAddedEvent) {
|
||||
wm.Email = e.EmailAddress
|
||||
wm.Phone = e.PhoneNumber
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.PasswordEncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordEncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordChangeRequired = e.ChangeRequired
|
||||
}
|
||||
|
||||
@ -485,7 +485,7 @@ func (wm *UserV2WriteModel) reduceHumanRegisteredEvent(e *user.HumanRegisteredEv
|
||||
wm.Email = e.EmailAddress
|
||||
wm.Phone = e.PhoneNumber
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.PasswordEncodedHash = user.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordEncodedHash = crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash)
|
||||
wm.PasswordChangeRequired = e.ChangeRequired
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func (c *Commands) verifyUserPasskeyCode(ctx context.Context, userID, resourceOw
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = verifyCryptoCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg, wm.ChangeDate, wm.Expiration, wm.CryptoCode, code)
|
||||
err = verifyEncryptedCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg, wm.ChangeDate, wm.Expiration, wm.CryptoCode, code) //nolint:staticcheck
|
||||
if err != nil || wm.State != domain.PasswordlessInitCodeStateActive {
|
||||
c.verifyUserPasskeyCodeFailed(ctx, wm)
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Eeb2a", "Errors.User.Code.Invalid")
|
||||
@ -156,6 +156,6 @@ func (c *Commands) addUserPasskeyCode(ctx context.Context, userID, resourceOwner
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Commands) newPasskeyCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*CryptoCode, error) {
|
||||
return c.newCode(ctx, filter, domain.SecretGeneratorTypePasswordlessInitCode, alg)
|
||||
func (c *Commands) newPasskeyCode(ctx context.Context, filter preparation.FilterToQueryReducer, alg crypto.EncryptionAlgorithm) (*EncryptedCode, error) {
|
||||
return c.newEncryptedCode(ctx, filter, domain.SecretGeneratorTypePasswordlessInitCode, alg)
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func TestCommands_RegisterUserPasskeyWithCode(t *testing.T) {
|
||||
es := eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
)
|
||||
code, err := newCryptoCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg)
|
||||
code, err := newEncryptedCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) //nolint:staticcheck
|
||||
require.NoError(t, err)
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
@ -236,7 +236,7 @@ func TestCommands_verifyUserPasskeyCode(t *testing.T) {
|
||||
es := eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
)
|
||||
code, err := newCryptoCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg)
|
||||
code, err := newEncryptedCode(ctx, es.Filter, domain.SecretGeneratorTypePasswordlessInitCode, alg) //nolint:staticcheck
|
||||
require.NoError(t, err)
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
|
||||
@ -457,7 +457,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
@ -475,7 +475,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Hour),
|
||||
newCode: mockEncryptedCode("passkey1", time.Hour),
|
||||
eventstore: expectEventstore(),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
},
|
||||
@ -488,7 +488,7 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Minute),
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@ -530,9 +530,9 @@ func TestCommands_AddUserPasskeyCode(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.AddUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@ -546,7 +546,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
|
||||
type fields struct {
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
@ -565,7 +565,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
{
|
||||
name: "template error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args: args{
|
||||
@ -578,7 +578,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
{
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
},
|
||||
@ -592,7 +592,7 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
{
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Minute),
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@ -638,9 +638,9 @@ func TestCommands_AddUserPasskeyCodeURLTemplate(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.AddUserPasskeyCodeURLTemplate(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, tt.args.urlTmpl)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@ -653,7 +653,7 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
@ -671,7 +671,7 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
{
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
},
|
||||
@ -684,7 +684,7 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
{
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Minute),
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@ -730,9 +730,9 @@ func TestCommands_AddUserPasskeyCodeReturn(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.AddUserPasskeyCodeReturn(context.Background(), tt.args.userID, tt.args.resourceOwner, alg)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
@ -745,7 +745,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
alg := crypto.CreateMockEncryptionAlg(gomock.NewController(t))
|
||||
userAgg := &user.NewAggregate("user1", "org1").Aggregate
|
||||
type fields struct {
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
@ -763,7 +763,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "id generator error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectError(t, io.ErrClosedPipe),
|
||||
},
|
||||
@ -776,7 +776,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "crypto error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t, expectFilterError(io.ErrClosedPipe)),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "123"),
|
||||
},
|
||||
@ -789,7 +789,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "filter query error",
|
||||
fields: fields{
|
||||
newCode: newCryptoCode,
|
||||
newCode: newEncryptedCode,
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(testSecretGeneratorAddedEvent(domain.SecretGeneratorTypePasswordlessInitCode))),
|
||||
expectFilterError(io.ErrClosedPipe),
|
||||
@ -805,7 +805,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "push error",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Minute),
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@ -844,7 +844,7 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
{
|
||||
name: "success",
|
||||
fields: fields{
|
||||
newCode: mockCode("passkey1", time.Minute),
|
||||
newCode: mockEncryptedCode("passkey1", time.Minute),
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@ -890,9 +890,9 @@ func TestCommands_addUserPasskeyCode(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
newCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := c.addUserPasskeyCode(context.Background(), tt.args.userID, tt.args.resourceOwner, alg, "", false)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
|
@ -55,7 +55,7 @@ func (c *Commands) requestPasswordReset(ctx context.Context, userID string, retu
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
code, err := c.newCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordResetCode, c.userEncryption)
|
||||
code, err := c.newEncryptedCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypePasswordResetCode, c.userEncryption) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
checkPermission domain.PermissionCheck
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
newCode cryptoCodeFunc
|
||||
newCode encrypedCodeFunc
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@ -452,7 +452,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("code", 10*time.Minute),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -492,7 +492,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("code", 10*time.Minute),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -533,7 +533,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("code", 10*time.Minute),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -575,7 +575,7 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockCode("code", 10*time.Minute),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@ -593,10 +593,10 @@ func TestCommands_requestPasswordReset(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
newCode: tt.fields.newCode,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
}
|
||||
got, gotPlainCode, err := c.requestPasswordReset(tt.args.ctx, tt.args.userID, tt.args.returnCode, tt.args.urlTmpl, tt.args.notificationType)
|
||||
require.ErrorIs(t, err, tt.res.err)
|
||||
|
@ -55,7 +55,7 @@ func (c *Commands) ResendUserPhoneCodeReturnCode(ctx context.Context, userID str
|
||||
}
|
||||
|
||||
func (c *Commands) changeUserPhoneWithCode(ctx context.Context, userID, phone string, alg crypto.EncryptionAlgorithm, returnCode bool) (*domain.Phone, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode)
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -64,7 +64,7 @@ func (c *Commands) changeUserPhoneWithCode(ctx context.Context, userID, phone st
|
||||
}
|
||||
|
||||
func (c *Commands) resendUserPhoneCode(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm, returnCode bool) (*domain.Phone, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode) //nolint:staticcheck
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -117,7 +117,7 @@ func (c *Commands) resendUserPhoneCodeWithGenerator(ctx context.Context, userID
|
||||
}
|
||||
|
||||
func (c *Commands) VerifyUserPhone(ctx context.Context, userID, code string, alg crypto.EncryptionAlgorithm) (*domain.ObjectDetails, error) {
|
||||
config, err := secretGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode)
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -216,7 +216,7 @@ func (c *UserPhoneEvents) VerifyCode(ctx context.Context, code string, gen crypt
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-Fia4a", "Errors.User.Code.Empty")
|
||||
}
|
||||
|
||||
err := crypto.VerifyCode(c.model.CodeCreationDate, c.model.CodeExpiry, c.model.Code, code, gen)
|
||||
err := crypto.VerifyCode(c.model.CodeCreationDate, c.model.CodeExpiry, c.model.Code, code, gen.Alg())
|
||||
if err == nil {
|
||||
c.events = append(c.events, user.NewHumanPhoneVerifiedEvent(ctx, c.aggregate))
|
||||
return nil
|
||||
|
@ -8,7 +8,8 @@ import (
|
||||
|
||||
type SystemDefaults struct {
|
||||
SecretGenerators SecretGenerators
|
||||
PasswordHasher crypto.PasswordHashConfig
|
||||
PasswordHasher crypto.HashConfig
|
||||
SecretHasher crypto.HashConfig
|
||||
Multifactors MultifactorConfig
|
||||
DomainVerification DomainVerification
|
||||
Notifications Notifications
|
||||
@ -16,7 +17,6 @@ type SystemDefaults struct {
|
||||
}
|
||||
|
||||
type SecretGenerators struct {
|
||||
PasswordSaltCost int
|
||||
MachineKeySize uint32
|
||||
ApplicationKeySize uint32
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var _ HashAlgorithm = (*BCrypt)(nil)
|
||||
|
||||
type BCrypt struct {
|
||||
cost int
|
||||
}
|
||||
|
||||
func NewBCrypt(cost int) *BCrypt {
|
||||
return &BCrypt{cost: cost}
|
||||
}
|
||||
|
||||
func (b *BCrypt) Algorithm() string {
|
||||
return "bcrypt"
|
||||
}
|
||||
|
||||
func (b *BCrypt) Hash(value []byte) ([]byte, error) {
|
||||
return bcrypt.GenerateFromPassword(value, b.cost)
|
||||
}
|
||||
|
||||
func (b *BCrypt) CompareHash(hashed, value []byte) error {
|
||||
return bcrypt.CompareHashAndPassword(hashed, value)
|
||||
}
|
@ -26,7 +26,7 @@ type GeneratorConfig struct {
|
||||
type Generator interface {
|
||||
Length() uint
|
||||
Expiry() time.Duration
|
||||
Alg() Crypto
|
||||
Alg() EncryptionAlgorithm
|
||||
Runes() []rune
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ type encryptionGenerator struct {
|
||||
alg EncryptionAlgorithm
|
||||
}
|
||||
|
||||
func (g *encryptionGenerator) Alg() Crypto {
|
||||
func (g *encryptionGenerator) Alg() EncryptionAlgorithm {
|
||||
return g.alg
|
||||
}
|
||||
|
||||
@ -64,22 +64,30 @@ func NewEncryptionGenerator(config GeneratorConfig, algorithm EncryptionAlgorith
|
||||
}
|
||||
}
|
||||
|
||||
type hashGenerator struct {
|
||||
type HashGenerator struct {
|
||||
generator
|
||||
alg HashAlgorithm
|
||||
hasher *Hasher
|
||||
}
|
||||
|
||||
func (g *hashGenerator) Alg() Crypto {
|
||||
return g.alg
|
||||
}
|
||||
|
||||
func NewHashGenerator(config GeneratorConfig, algorithm HashAlgorithm) Generator {
|
||||
return &hashGenerator{
|
||||
func NewHashGenerator(config GeneratorConfig, hasher *Hasher) *HashGenerator {
|
||||
return &HashGenerator{
|
||||
newGenerator(config),
|
||||
algorithm,
|
||||
hasher,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *HashGenerator) NewCode() (encoded, plain string, err error) {
|
||||
plain, err = GenerateRandomString(g.Length(), g.Runes())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
encoded, err = g.hasher.Hash(plain)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return encoded, plain, nil
|
||||
}
|
||||
|
||||
func newGenerator(config GeneratorConfig) generator {
|
||||
var runes []rune
|
||||
if config.IncludeLowerLetters {
|
||||
@ -120,21 +128,11 @@ func IsCodeExpired(creationDate time.Time, expiry time.Duration) bool {
|
||||
return creationDate.Add(expiry).Before(time.Now().UTC())
|
||||
}
|
||||
|
||||
func VerifyCode(creationDate time.Time, expiry time.Duration, cryptoCode *CryptoValue, verificationCode string, g Generator) error {
|
||||
return VerifyCodeWithAlgorithm(creationDate, expiry, cryptoCode, verificationCode, g.Alg())
|
||||
}
|
||||
|
||||
func VerifyCodeWithAlgorithm(creationDate time.Time, expiry time.Duration, cryptoCode *CryptoValue, verificationCode string, algorithm Crypto) error {
|
||||
func VerifyCode(creationDate time.Time, expiry time.Duration, cryptoCode *CryptoValue, verificationCode string, algorithm EncryptionAlgorithm) error {
|
||||
if IsCodeExpired(creationDate, expiry) {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "CODE-QvUQ4P", "Errors.User.Code.Expired")
|
||||
}
|
||||
switch alg := algorithm.(type) {
|
||||
case EncryptionAlgorithm:
|
||||
return verifyEncryptedCode(cryptoCode, verificationCode, alg)
|
||||
case HashAlgorithm:
|
||||
return verifyHashedCode(cryptoCode, verificationCode, alg)
|
||||
}
|
||||
return zerrors.ThrowInvalidArgument(nil, "CODE-fW2gNa", "Errors.User.Code.GeneratorAlgNotSupported")
|
||||
return verifyEncryptedCode(cryptoCode, verificationCode, algorithm)
|
||||
}
|
||||
|
||||
func GenerateRandomString(length uint, chars []rune) (string, error) {
|
||||
@ -173,10 +171,3 @@ func verifyEncryptedCode(cryptoCode *CryptoValue, verificationCode string, alg E
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyHashedCode(cryptoCode *CryptoValue, verificationCode string, alg HashAlgorithm) error {
|
||||
if cryptoCode == nil {
|
||||
return zerrors.ThrowInvalidArgument(nil, "CRYPT-2q3r", "cryptoCode must not be nil")
|
||||
}
|
||||
return CompareHash(cryptoCode, []byte(verificationCode), alg)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
//
|
||||
// mockgen -source code.go -destination ./code_mock.go -package crypto
|
||||
//
|
||||
|
||||
// Package crypto is a generated GoMock package.
|
||||
package crypto
|
||||
|
||||
@ -39,10 +40,10 @@ func (m *MockGenerator) EXPECT() *MockGeneratorMockRecorder {
|
||||
}
|
||||
|
||||
// Alg mocks base method.
|
||||
func (m *MockGenerator) Alg() Crypto {
|
||||
func (m *MockGenerator) Alg() EncryptionAlgorithm {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Alg")
|
||||
ret0, _ := ret[0].(Crypto)
|
||||
ret0, _ := ret[0].(EncryptionAlgorithm)
|
||||
return ret0
|
||||
}
|
||||
|
||||
|
@ -60,32 +60,13 @@ func createMockEncryptionAlgorithm(ctrl *gomock.Controller, encryptFunction func
|
||||
return mCrypto
|
||||
}
|
||||
|
||||
func CreateMockHashAlg(ctrl *gomock.Controller) HashAlgorithm {
|
||||
mCrypto := NewMockHashAlgorithm(ctrl)
|
||||
mCrypto.EXPECT().Algorithm().AnyTimes().Return("hash")
|
||||
mCrypto.EXPECT().Hash(gomock.Any()).AnyTimes().DoAndReturn(
|
||||
func(code []byte) ([]byte, error) {
|
||||
return code, nil
|
||||
},
|
||||
)
|
||||
mCrypto.EXPECT().CompareHash(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(
|
||||
func(hashed, comparer []byte) error {
|
||||
if string(hashed) != string(comparer) {
|
||||
return zerrors.ThrowInternal(nil, "id", "invalid")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return mCrypto
|
||||
}
|
||||
|
||||
func createMockCrypto(t *testing.T) Crypto {
|
||||
mCrypto := NewMockCrypto(gomock.NewController(t))
|
||||
func createMockCrypto(t *testing.T) EncryptionAlgorithm {
|
||||
mCrypto := NewMockEncryptionAlgorithm(gomock.NewController(t))
|
||||
mCrypto.EXPECT().Algorithm().AnyTimes().Return("crypto")
|
||||
return mCrypto
|
||||
}
|
||||
|
||||
func createMockGenerator(t *testing.T, crypto Crypto) Generator {
|
||||
func createMockGenerator(t *testing.T, crypto EncryptionAlgorithm) Generator {
|
||||
mGenerator := NewMockGenerator(gomock.NewController(t))
|
||||
mGenerator.EXPECT().Alg().AnyTimes().Return(crypto)
|
||||
return mGenerator
|
||||
|
@ -102,25 +102,10 @@ func TestVerifyCode(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"hash alg ok",
|
||||
args{
|
||||
creationDate: time.Now(),
|
||||
expiry: 5 * time.Minute,
|
||||
cryptoCode: &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: "hash",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
verificationCode: "code",
|
||||
g: createMockGenerator(t, CreateMockHashAlg(gomock.NewController(t))),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := VerifyCode(tt.args.creationDate, tt.args.expiry, tt.args.cryptoCode, tt.args.verificationCode, tt.args.g); (err != nil) != tt.wantErr {
|
||||
if err := VerifyCode(tt.args.creationDate, tt.args.expiry, tt.args.cryptoCode, tt.args.verificationCode, tt.args.g.Alg()); (err != nil) != tt.wantErr {
|
||||
t.Errorf("VerifyCode() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
@ -222,85 +207,3 @@ func Test_verifyEncryptedCode(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_verifyHashedCode(t *testing.T) {
|
||||
type args struct {
|
||||
cryptoCode *CryptoValue
|
||||
verificationCode string
|
||||
alg HashAlgorithm
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
|
||||
{
|
||||
"nil error",
|
||||
args{
|
||||
cryptoCode: nil,
|
||||
verificationCode: "",
|
||||
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrong cryptotype error",
|
||||
args{
|
||||
cryptoCode: &CryptoValue{
|
||||
CryptoType: TypeEncryption,
|
||||
Crypted: nil,
|
||||
},
|
||||
verificationCode: "",
|
||||
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrong algorithm error",
|
||||
args{
|
||||
cryptoCode: &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: "hash2",
|
||||
Crypted: nil,
|
||||
},
|
||||
verificationCode: "",
|
||||
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrong verification code error",
|
||||
args{
|
||||
cryptoCode: &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: "hash",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
verificationCode: "wrong",
|
||||
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"verification code ok",
|
||||
args{
|
||||
cryptoCode: &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: "hash",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
verificationCode: "code",
|
||||
alg: CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := verifyHashedCode(tt.args.cryptoCode, tt.args.verificationCode, tt.args.alg); (err != nil) != tt.wantErr {
|
||||
t.Errorf("verifyHashedCode() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,8 @@ const (
|
||||
TypeHash // Depcrecated: use [passwap.Swapper] instead
|
||||
)
|
||||
|
||||
type Crypto interface {
|
||||
Algorithm() string
|
||||
}
|
||||
|
||||
type EncryptionAlgorithm interface {
|
||||
Crypto
|
||||
Algorithm() string
|
||||
EncryptionKeyID() string
|
||||
DecryptionKeyIDs() []string
|
||||
Encrypt(value []byte) ([]byte, error)
|
||||
@ -26,13 +22,6 @@ type EncryptionAlgorithm interface {
|
||||
DecryptString(hashed []byte, keyID string) (string, error)
|
||||
}
|
||||
|
||||
// Depcrecated: use [passwap.Swapper] instead
|
||||
type HashAlgorithm interface {
|
||||
Crypto
|
||||
Hash(value []byte) ([]byte, error)
|
||||
CompareHash(hashed, comparer []byte) error
|
||||
}
|
||||
|
||||
type CryptoValue struct {
|
||||
CryptoType CryptoType
|
||||
Algorithm string
|
||||
@ -59,14 +48,8 @@ func (c *CryptoValue) Scan(src interface{}) error {
|
||||
|
||||
type CryptoType int
|
||||
|
||||
func Crypt(value []byte, c Crypto) (*CryptoValue, error) {
|
||||
switch alg := c.(type) {
|
||||
case EncryptionAlgorithm:
|
||||
return Encrypt(value, alg)
|
||||
case HashAlgorithm:
|
||||
return Hash(value, alg)
|
||||
}
|
||||
return nil, zerrors.ThrowInternal(nil, "CRYPT-r4IaHZ", "algorithm not supported")
|
||||
func Crypt(value []byte, alg EncryptionAlgorithm) (*CryptoValue, error) {
|
||||
return Encrypt(value, alg)
|
||||
}
|
||||
|
||||
func Encrypt(value []byte, alg EncryptionAlgorithm) (*CryptoValue, error) {
|
||||
@ -108,33 +91,6 @@ func checkEncryptionAlgorithm(value *CryptoValue, alg EncryptionAlgorithm) error
|
||||
return zerrors.ThrowInvalidArgument(nil, "CRYPT-Kq12vn", "value was encrypted with a different key")
|
||||
}
|
||||
|
||||
func Hash(value []byte, alg HashAlgorithm) (*CryptoValue, error) {
|
||||
hashed, err := alg.Hash(value)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "CRYPT-rBVaJU", "error hashing value")
|
||||
}
|
||||
return &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: alg.Algorithm(),
|
||||
Crypted: hashed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func CompareHash(value *CryptoValue, comparer []byte, alg HashAlgorithm) error {
|
||||
if value.Algorithm != alg.Algorithm() {
|
||||
return zerrors.ThrowInvalidArgument(nil, "CRYPT-HF32f", "value was hashed with a different algorithm")
|
||||
}
|
||||
return alg.CompareHash(value.Crypted, comparer)
|
||||
}
|
||||
|
||||
func FillHash(value []byte, alg HashAlgorithm) *CryptoValue {
|
||||
return &CryptoValue{
|
||||
CryptoType: TypeHash,
|
||||
Algorithm: alg.Algorithm(),
|
||||
Crypted: value,
|
||||
}
|
||||
}
|
||||
|
||||
func CheckToken(alg EncryptionAlgorithm, token string, content string) error {
|
||||
if token == "" {
|
||||
return zerrors.ThrowPermissionDenied(nil, "CRYPTO-Sfefs", "Errors.Intent.InvalidToken")
|
||||
@ -152,3 +108,12 @@ func CheckToken(alg EncryptionAlgorithm, token string, content string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SecretOrEncodedHash returns the Crypted value from legacy [CryptoValue] if it is not nil.
|
||||
// otherwise it will returns the encoded hash string.
|
||||
func SecretOrEncodedHash(secret *CryptoValue, encoded string) string {
|
||||
if secret != nil {
|
||||
return string(secret.Crypted)
|
||||
}
|
||||
return encoded
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
//
|
||||
// mockgen -source crypto.go -destination ./crypto_mock.go -package crypto
|
||||
//
|
||||
|
||||
// Package crypto is a generated GoMock package.
|
||||
package crypto
|
||||
|
||||
@ -14,43 +15,6 @@ import (
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockCrypto is a mock of Crypto interface.
|
||||
type MockCrypto struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockCryptoMockRecorder
|
||||
}
|
||||
|
||||
// MockCryptoMockRecorder is the mock recorder for MockCrypto.
|
||||
type MockCryptoMockRecorder struct {
|
||||
mock *MockCrypto
|
||||
}
|
||||
|
||||
// NewMockCrypto creates a new mock instance.
|
||||
func NewMockCrypto(ctrl *gomock.Controller) *MockCrypto {
|
||||
mock := &MockCrypto{ctrl: ctrl}
|
||||
mock.recorder = &MockCryptoMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockCrypto) EXPECT() *MockCryptoMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Algorithm mocks base method.
|
||||
func (m *MockCrypto) Algorithm() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Algorithm")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Algorithm indicates an expected call of Algorithm.
|
||||
func (mr *MockCryptoMockRecorder) Algorithm() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Algorithm", reflect.TypeOf((*MockCrypto)(nil).Algorithm))
|
||||
}
|
||||
|
||||
// MockEncryptionAlgorithm is a mock of EncryptionAlgorithm interface.
|
||||
type MockEncryptionAlgorithm struct {
|
||||
ctrl *gomock.Controller
|
||||
@ -160,69 +124,3 @@ func (mr *MockEncryptionAlgorithmMockRecorder) EncryptionKeyID() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptionKeyID", reflect.TypeOf((*MockEncryptionAlgorithm)(nil).EncryptionKeyID))
|
||||
}
|
||||
|
||||
// MockHashAlgorithm is a mock of HashAlgorithm interface.
|
||||
type MockHashAlgorithm struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockHashAlgorithmMockRecorder
|
||||
}
|
||||
|
||||
// MockHashAlgorithmMockRecorder is the mock recorder for MockHashAlgorithm.
|
||||
type MockHashAlgorithmMockRecorder struct {
|
||||
mock *MockHashAlgorithm
|
||||
}
|
||||
|
||||
// NewMockHashAlgorithm creates a new mock instance.
|
||||
func NewMockHashAlgorithm(ctrl *gomock.Controller) *MockHashAlgorithm {
|
||||
mock := &MockHashAlgorithm{ctrl: ctrl}
|
||||
mock.recorder = &MockHashAlgorithmMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockHashAlgorithm) EXPECT() *MockHashAlgorithmMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Algorithm mocks base method.
|
||||
func (m *MockHashAlgorithm) Algorithm() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Algorithm")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Algorithm indicates an expected call of Algorithm.
|
||||
func (mr *MockHashAlgorithmMockRecorder) Algorithm() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Algorithm", reflect.TypeOf((*MockHashAlgorithm)(nil).Algorithm))
|
||||
}
|
||||
|
||||
// CompareHash mocks base method.
|
||||
func (m *MockHashAlgorithm) CompareHash(hashed, comparer []byte) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CompareHash", hashed, comparer)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CompareHash indicates an expected call of CompareHash.
|
||||
func (mr *MockHashAlgorithmMockRecorder) CompareHash(hashed, comparer any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareHash", reflect.TypeOf((*MockHashAlgorithm)(nil).CompareHash), hashed, comparer)
|
||||
}
|
||||
|
||||
// Hash mocks base method.
|
||||
func (m *MockHashAlgorithm) Hash(value []byte) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Hash", value)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Hash indicates an expected call of Hash.
|
||||
func (mr *MockHashAlgorithmMockRecorder) Hash(value any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Hash", reflect.TypeOf((*MockHashAlgorithm)(nil).Hash), value)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (a *alg) Algorithm() string {
|
||||
func TestCrypt(t *testing.T) {
|
||||
type args struct {
|
||||
value []byte
|
||||
c Crypto
|
||||
c EncryptionAlgorithm
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -74,18 +74,6 @@ func TestCrypt(t *testing.T) {
|
||||
&CryptoValue{CryptoType: TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("test")},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"hash",
|
||||
args{[]byte("test"), &mockHashCrypto{}},
|
||||
&CryptoValue{CryptoType: TypeHash, Algorithm: "hash", Crypted: []byte("test")},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong type",
|
||||
args{[]byte("test"), &alg{}},
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -208,66 +196,3 @@ func TestDecryptString(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
type args struct {
|
||||
value []byte
|
||||
c HashAlgorithm
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *CryptoValue
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"ok",
|
||||
args{[]byte("test"), &mockHashCrypto{}},
|
||||
&CryptoValue{CryptoType: TypeHash, Algorithm: "hash", Crypted: []byte("test")},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Hash(tt.args.value, tt.args.c)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Hash() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Hash() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareHash(t *testing.T) {
|
||||
type args struct {
|
||||
value *CryptoValue
|
||||
comparer []byte
|
||||
c HashAlgorithm
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"ok",
|
||||
args{&CryptoValue{CryptoType: TypeHash, Algorithm: "hash", Crypted: []byte("test")}, []byte("test"), &mockHashCrypto{}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong",
|
||||
args{&CryptoValue{CryptoType: TypeHash, Algorithm: "hash", Crypted: []byte("test")}, []byte("test2"), &mockHashCrypto{}},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := CompareHash(tt.args.value, tt.args.comparer, tt.args.c); (err != nil) != tt.wantErr {
|
||||
t.Errorf("CompareHash() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,12 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type PasswordHasher struct {
|
||||
type Hasher struct {
|
||||
*passwap.Swapper
|
||||
Prefixes []string
|
||||
}
|
||||
|
||||
func (h *PasswordHasher) EncodingSupported(encodedHash string) bool {
|
||||
func (h *Hasher) EncodingSupported(encodedHash string) bool {
|
||||
for _, prefix := range h.Prefixes {
|
||||
if strings.HasPrefix(encodedHash, prefix) {
|
||||
return true
|
||||
@ -54,12 +54,12 @@ const (
|
||||
HashModeSHA512 HashMode = "sha512"
|
||||
)
|
||||
|
||||
type PasswordHashConfig struct {
|
||||
type HashConfig struct {
|
||||
Verifiers []HashName
|
||||
Hasher HasherConfig
|
||||
}
|
||||
|
||||
func (c *PasswordHashConfig) PasswordHasher() (*PasswordHasher, error) {
|
||||
func (c *HashConfig) NewHasher() (*Hasher, error) {
|
||||
verifiers, vPrefixes, err := c.buildVerifiers()
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "CRYPT-sahW9", "password hash config invalid")
|
||||
@ -68,7 +68,7 @@ func (c *PasswordHashConfig) PasswordHasher() (*PasswordHasher, error) {
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "CRYPT-Que4r", "password hash config invalid")
|
||||
}
|
||||
return &PasswordHasher{
|
||||
return &Hasher{
|
||||
Swapper: passwap.NewSwapper(hasher, verifiers...),
|
||||
Prefixes: append(hPrefixes, vPrefixes...),
|
||||
}, nil
|
||||
@ -105,7 +105,7 @@ var knowVerifiers = map[HashName]prefixVerifier{
|
||||
},
|
||||
}
|
||||
|
||||
func (c *PasswordHashConfig) buildVerifiers() (verifiers []verifier.Verifier, prefixes []string, err error) {
|
||||
func (c *HashConfig) buildVerifiers() (verifiers []verifier.Verifier, prefixes []string, err error) {
|
||||
verifiers = make([]verifier.Verifier, len(c.Verifiers))
|
||||
prefixes = make([]string, 0, len(c.Verifiers)+1)
|
||||
for i, name := range c.Verifiers {
|
||||
|
@ -49,7 +49,7 @@ func TestPasswordHasher_EncodingSupported(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
h := &PasswordHasher{
|
||||
h := &Hasher{
|
||||
Prefixes: []string{bcrypt.Prefix, argon2.Prefix},
|
||||
}
|
||||
got := h.EncodingSupported(tt.encodedHash)
|
||||
@ -340,11 +340,11 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &PasswordHashConfig{
|
||||
c := &HashConfig{
|
||||
Verifiers: tt.fields.Verifiers,
|
||||
Hasher: tt.fields.Hasher,
|
||||
}
|
||||
got, err := c.PasswordHasher()
|
||||
got, err := c.NewHasher()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
|
@ -11,7 +11,7 @@ type APIApp struct {
|
||||
AppID string
|
||||
AppName string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
EncodedHash string
|
||||
ClientSecretString string
|
||||
AuthMethodType APIAuthMethodType
|
||||
|
||||
@ -41,21 +41,21 @@ func (a *APIApp) setClientID(clientID string) {
|
||||
a.ClientID = clientID
|
||||
}
|
||||
|
||||
func (a *APIApp) setClientSecret(clientSecret *crypto.CryptoValue) {
|
||||
a.ClientSecret = clientSecret
|
||||
func (a *APIApp) setClientSecret(encodedHash string) {
|
||||
a.EncodedHash = encodedHash
|
||||
}
|
||||
|
||||
func (a *APIApp) requiresClientSecret() bool {
|
||||
return a.AuthMethodType == APIAuthMethodTypeBasic
|
||||
}
|
||||
|
||||
func (a *APIApp) GenerateClientSecretIfNeeded(generator crypto.Generator) (secret string, err error) {
|
||||
func (a *APIApp) GenerateClientSecretIfNeeded(generator *crypto.HashGenerator) (plain string, err error) {
|
||||
if a.AuthMethodType == APIAuthMethodTypePrivateKeyJWT {
|
||||
return "", nil
|
||||
}
|
||||
a.ClientSecret, secret, err = NewClientSecret(generator)
|
||||
a.EncodedHash, plain, err = generator.NewCode()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return secret, nil
|
||||
return plain, nil
|
||||
}
|
||||
|
@ -4,16 +4,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type oAuthApplication interface {
|
||||
setClientID(clientID string)
|
||||
setClientSecret(secret *crypto.CryptoValue)
|
||||
setClientSecret(encodedHash string)
|
||||
requiresClientSecret() bool
|
||||
}
|
||||
|
||||
@ -37,23 +33,14 @@ func NewClientID(idGenerator id.Generator, projectName string) (string, error) {
|
||||
return fmt.Sprintf("%s@%s", rndID, strings.ReplaceAll(strings.ToLower(projectName), " ", "_")), nil
|
||||
}
|
||||
|
||||
func SetNewClientSecretIfNeeded(a oAuthApplication, generator crypto.Generator) (string, error) {
|
||||
func SetNewClientSecretIfNeeded(a oAuthApplication, generate func() (encodedHash, plain string, err error)) (string, error) {
|
||||
if !a.requiresClientSecret() {
|
||||
return "", nil
|
||||
}
|
||||
clientSecret, secretString, err := NewClientSecret(generator)
|
||||
encodedHash, plain, err := generate()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
a.setClientSecret(clientSecret)
|
||||
return secretString, nil
|
||||
}
|
||||
|
||||
func NewClientSecret(generator crypto.Generator) (*crypto.CryptoValue, string, error) {
|
||||
cryptoValue, stringSecret, err := crypto.NewCode(generator)
|
||||
if err != nil {
|
||||
logging.Log("MODEL-UpnTI").OnError(err).Error("unable to create client secret")
|
||||
return nil, "", zerrors.ThrowInternal(err, "MODEL-gH2Wl", "Errors.Project.CouldNotGenerateClientSecret")
|
||||
}
|
||||
return cryptoValue, stringSecret, nil
|
||||
a.setClientSecret(encodedHash)
|
||||
return plain, nil
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
)
|
||||
|
||||
@ -28,7 +27,7 @@ type OIDCApp struct {
|
||||
AppID string
|
||||
AppName string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
EncodedHash string
|
||||
ClientSecretString string
|
||||
RedirectUris []string
|
||||
ResponseTypes []OIDCResponseType
|
||||
@ -62,8 +61,8 @@ func (a *OIDCApp) setClientID(clientID string) {
|
||||
a.ClientID = clientID
|
||||
}
|
||||
|
||||
func (a *OIDCApp) setClientSecret(clientSecret *crypto.CryptoValue) {
|
||||
a.ClientSecret = clientSecret
|
||||
func (a *OIDCApp) setClientSecret(encodedHash string) {
|
||||
a.EncodedHash = encodedHash
|
||||
}
|
||||
|
||||
func (a *OIDCApp) requiresClientSecret() bool {
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -102,10 +104,10 @@ func (u *Human) EnsureDisplayName() {
|
||||
u.DisplayName = u.Username
|
||||
}
|
||||
|
||||
func (u *Human) HashPasswordIfExisting(policy *PasswordComplexityPolicy, hasher *crypto.PasswordHasher, onetime bool) error {
|
||||
func (u *Human) HashPasswordIfExisting(ctx context.Context, policy *PasswordComplexityPolicy, hasher *crypto.Hasher, onetime bool) error {
|
||||
if u.Password != nil {
|
||||
u.Password.ChangeRequired = onetime
|
||||
return u.Password.HashPasswordIfExisting(policy, hasher)
|
||||
return u.Password.HashPasswordIfExisting(ctx, policy, hasher)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
@ -30,7 +32,7 @@ type PasswordCode struct {
|
||||
NotificationType NotificationType
|
||||
}
|
||||
|
||||
func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, hasher *crypto.PasswordHasher) error {
|
||||
func (p *Password) HashPasswordIfExisting(ctx context.Context, policy *PasswordComplexityPolicy, hasher *crypto.Hasher) error {
|
||||
if p.SecretString == "" {
|
||||
return nil
|
||||
}
|
||||
@ -40,7 +42,9 @@ func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, hash
|
||||
if err := policy.Check(p.SecretString); err != nil {
|
||||
return err
|
||||
}
|
||||
_, spanHash := tracing.NewNamedSpan(ctx, "passwap.Hash")
|
||||
encoded, err := hasher.Hash(p.SecretString)
|
||||
spanHash.EndWithError(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func NewMachineClientSecret(generator crypto.Generator) (*crypto.CryptoValue, string, error) {
|
||||
cryptoValue, stringSecret, err := crypto.NewCode(generator)
|
||||
if err != nil {
|
||||
return nil, "", zerrors.ThrowInternal(err, "MODEL-57cjsiw", "Errors.User.Machine.Secret.CouldNotGenerate")
|
||||
}
|
||||
return cryptoValue, stringSecret, nil
|
||||
}
|
@ -15,98 +15,98 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps6.id,` +
|
||||
` projections.apps6.name,` +
|
||||
` projections.apps6.project_id,` +
|
||||
` projections.apps6.creation_date,` +
|
||||
` projections.apps6.change_date,` +
|
||||
` projections.apps6.resource_owner,` +
|
||||
` projections.apps6.state,` +
|
||||
` projections.apps6.sequence,` +
|
||||
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
|
||||
` projections.apps7.name,` +
|
||||
` projections.apps7.project_id,` +
|
||||
` projections.apps7.creation_date,` +
|
||||
` projections.apps7.change_date,` +
|
||||
` projections.apps7.resource_owner,` +
|
||||
` projections.apps7.state,` +
|
||||
` projections.apps7.sequence,` +
|
||||
// api config
|
||||
` projections.apps6_api_configs.app_id,` +
|
||||
` projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_api_configs.auth_method,` +
|
||||
` projections.apps7_api_configs.app_id,` +
|
||||
` projections.apps7_api_configs.client_id,` +
|
||||
` projections.apps7_api_configs.auth_method,` +
|
||||
// oidc config
|
||||
` projections.apps6_oidc_configs.app_id,` +
|
||||
` projections.apps6_oidc_configs.version,` +
|
||||
` projections.apps6_oidc_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.response_types,` +
|
||||
` projections.apps6_oidc_configs.grant_types,` +
|
||||
` projections.apps6_oidc_configs.application_type,` +
|
||||
` projections.apps6_oidc_configs.auth_method_type,` +
|
||||
` projections.apps6_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps6_oidc_configs.access_token_type,` +
|
||||
` projections.apps6_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps6_oidc_configs.clock_skew,` +
|
||||
` projections.apps6_oidc_configs.additional_origins,` +
|
||||
` projections.apps6_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.app_id,` +
|
||||
` projections.apps7_oidc_configs.version,` +
|
||||
` projections.apps7_oidc_configs.client_id,` +
|
||||
` projections.apps7_oidc_configs.redirect_uris,` +
|
||||
` projections.apps7_oidc_configs.response_types,` +
|
||||
` projections.apps7_oidc_configs.grant_types,` +
|
||||
` projections.apps7_oidc_configs.application_type,` +
|
||||
` projections.apps7_oidc_configs.auth_method_type,` +
|
||||
` projections.apps7_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps7_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps7_oidc_configs.access_token_type,` +
|
||||
` projections.apps7_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps7_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps7_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps7_oidc_configs.clock_skew,` +
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
//saml config
|
||||
` projections.apps6_saml_configs.app_id,` +
|
||||
` projections.apps6_saml_configs.entity_id,` +
|
||||
` projections.apps6_saml_configs.metadata,` +
|
||||
` projections.apps6_saml_configs.metadata_url` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
` projections.apps7_saml_configs.metadata,` +
|
||||
` projections.apps7_saml_configs.metadata_url` +
|
||||
` FROM projections.apps7` +
|
||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps6.id,` +
|
||||
` projections.apps6.name,` +
|
||||
` projections.apps6.project_id,` +
|
||||
` projections.apps6.creation_date,` +
|
||||
` projections.apps6.change_date,` +
|
||||
` projections.apps6.resource_owner,` +
|
||||
` projections.apps6.state,` +
|
||||
` projections.apps6.sequence,` +
|
||||
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
|
||||
` projections.apps7.name,` +
|
||||
` projections.apps7.project_id,` +
|
||||
` projections.apps7.creation_date,` +
|
||||
` projections.apps7.change_date,` +
|
||||
` projections.apps7.resource_owner,` +
|
||||
` projections.apps7.state,` +
|
||||
` projections.apps7.sequence,` +
|
||||
// api config
|
||||
` projections.apps6_api_configs.app_id,` +
|
||||
` projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_api_configs.auth_method,` +
|
||||
` projections.apps7_api_configs.app_id,` +
|
||||
` projections.apps7_api_configs.client_id,` +
|
||||
` projections.apps7_api_configs.auth_method,` +
|
||||
// oidc config
|
||||
` projections.apps6_oidc_configs.app_id,` +
|
||||
` projections.apps6_oidc_configs.version,` +
|
||||
` projections.apps6_oidc_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.response_types,` +
|
||||
` projections.apps6_oidc_configs.grant_types,` +
|
||||
` projections.apps6_oidc_configs.application_type,` +
|
||||
` projections.apps6_oidc_configs.auth_method_type,` +
|
||||
` projections.apps6_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps6_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps6_oidc_configs.access_token_type,` +
|
||||
` projections.apps6_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps6_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps6_oidc_configs.clock_skew,` +
|
||||
` projections.apps6_oidc_configs.additional_origins,` +
|
||||
` projections.apps6_oidc_configs.skip_native_app_success_page,` +
|
||||
` projections.apps7_oidc_configs.app_id,` +
|
||||
` projections.apps7_oidc_configs.version,` +
|
||||
` projections.apps7_oidc_configs.client_id,` +
|
||||
` projections.apps7_oidc_configs.redirect_uris,` +
|
||||
` projections.apps7_oidc_configs.response_types,` +
|
||||
` projections.apps7_oidc_configs.grant_types,` +
|
||||
` projections.apps7_oidc_configs.application_type,` +
|
||||
` projections.apps7_oidc_configs.auth_method_type,` +
|
||||
` projections.apps7_oidc_configs.post_logout_redirect_uris,` +
|
||||
` projections.apps7_oidc_configs.is_dev_mode,` +
|
||||
` projections.apps7_oidc_configs.access_token_type,` +
|
||||
` projections.apps7_oidc_configs.access_token_role_assertion,` +
|
||||
` projections.apps7_oidc_configs.id_token_role_assertion,` +
|
||||
` projections.apps7_oidc_configs.id_token_userinfo_assertion,` +
|
||||
` projections.apps7_oidc_configs.clock_skew,` +
|
||||
` projections.apps7_oidc_configs.additional_origins,` +
|
||||
` projections.apps7_oidc_configs.skip_native_app_success_page,` +
|
||||
//saml config
|
||||
` projections.apps6_saml_configs.app_id,` +
|
||||
` projections.apps6_saml_configs.entity_id,` +
|
||||
` projections.apps6_saml_configs.metadata,` +
|
||||
` projections.apps6_saml_configs.metadata_url,` +
|
||||
` projections.apps7_saml_configs.app_id,` +
|
||||
` projections.apps7_saml_configs.entity_id,` +
|
||||
` projections.apps7_saml_configs.metadata,` +
|
||||
` projections.apps7_saml_configs.metadata_url,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` FROM projections.apps7` +
|
||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps6_api_configs.client_id,` +
|
||||
` projections.apps6_oidc_configs.client_id` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
expectedAppIDsQuery = regexp.QuoteMeta(`SELECT projections.apps7_api_configs.client_id,` +
|
||||
` projections.apps7_oidc_configs.client_id` +
|
||||
` FROM projections.apps7` +
|
||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps6.project_id` +
|
||||
` FROM projections.apps6` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
expectedProjectIDByAppQuery = regexp.QuoteMeta(`SELECT projections.apps7.project_id` +
|
||||
` FROM projections.apps7` +
|
||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
expectedProjectByAppQuery = regexp.QuoteMeta(`SELECT projections.projects4.id,` +
|
||||
` projections.projects4.creation_date,` +
|
||||
@ -120,10 +120,10 @@ var (
|
||||
` projections.projects4.has_project_check,` +
|
||||
` projections.projects4.private_labeling_setting` +
|
||||
` FROM projections.projects4` +
|
||||
` JOIN projections.apps6 ON projections.projects4.id = projections.apps6.project_id AND projections.projects4.instance_id = projections.apps6.instance_id` +
|
||||
` LEFT JOIN projections.apps6_api_configs ON projections.apps6.id = projections.apps6_api_configs.app_id AND projections.apps6.instance_id = projections.apps6_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_oidc_configs ON projections.apps6.id = projections.apps6_oidc_configs.app_id AND projections.apps6.instance_id = projections.apps6_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps6_saml_configs ON projections.apps6.id = projections.apps6_saml_configs.app_id AND projections.apps6.instance_id = projections.apps6_saml_configs.instance_id` +
|
||||
` JOIN projections.apps7 ON projections.projects4.id = projections.apps7.project_id AND projections.projects4.instance_id = projections.apps7.instance_id` +
|
||||
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
|
||||
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
|
||||
` AS OF SYSTEM TIME '-1 ms'`)
|
||||
|
||||
appCols = database.TextArray[string]{
|
||||
|
@ -1,11 +1,11 @@
|
||||
with config as (
|
||||
select app_id, client_id, client_secret
|
||||
from projections.apps6_api_configs
|
||||
select app_id, client_id, client_secret, 'api' as app_type
|
||||
from projections.apps7_api_configs
|
||||
where instance_id = $1
|
||||
and client_id = $2
|
||||
union
|
||||
select app_id, client_id, client_secret
|
||||
from projections.apps6_oidc_configs
|
||||
select app_id, client_id, client_secret, 'oidc' as app_type
|
||||
from projections.apps7_oidc_configs
|
||||
where instance_id = $1
|
||||
and client_id = $2
|
||||
),
|
||||
@ -18,6 +18,7 @@ keys as (
|
||||
and expiration > current_timestamp
|
||||
group by identifier
|
||||
)
|
||||
select config.client_id, config.client_secret, apps.project_id, keys.public_keys from config
|
||||
join projections.apps6 apps on apps.id = config.app_id
|
||||
select config.app_id, config.client_id, config.client_secret, config.app_type, apps.project_id, apps.resource_owner, keys.public_keys
|
||||
from config
|
||||
join projections.apps7 apps on apps.id = config.app_id
|
||||
left join keys on keys.client_id = config.client_id;
|
||||
|
@ -7,8 +7,8 @@ with client as (
|
||||
c.application_type, c.auth_method_type, c.post_logout_redirect_uris, c.is_dev_mode,
|
||||
c.access_token_type, c.access_token_role_assertion, c.id_token_role_assertion,
|
||||
c.id_token_userinfo_assertion, c.clock_skew, c.additional_origins, a.project_id, a.state
|
||||
from projections.apps6_oidc_configs c
|
||||
join projections.apps6 a on a.id = c.app_id and a.instance_id = c.instance_id
|
||||
from projections.apps7_oidc_configs c
|
||||
join projections.apps7 a on a.id = c.app_id and a.instance_id = c.instance_id
|
||||
where c.instance_id = $1
|
||||
and c.client_id = $2
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
with usr as (
|
||||
select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name
|
||||
from projections.users11 u
|
||||
from projections.users12 u
|
||||
left join projections.login_names3 n on u.id = n.user_id and u.instance_id = n.instance_id
|
||||
where u.id = $1
|
||||
and u.instance_id = $2
|
||||
@ -9,7 +9,7 @@ with usr as (
|
||||
human as (
|
||||
select $1 as user_id, row_to_json(r) as human from (
|
||||
select first_name, last_name, nick_name, display_name, avatar_key, preferred_language, gender, email, is_email_verified, phone, is_phone_verified
|
||||
from projections.users11_humans
|
||||
from projections.users12_humans
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
) r
|
||||
@ -17,7 +17,7 @@ human as (
|
||||
machine as (
|
||||
select $1 as user_id, row_to_json(r) as machine from (
|
||||
select name, description
|
||||
from projections.users11_machines
|
||||
from projections.users12_machines
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
) r
|
||||
|
@ -21,21 +21,21 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users11_humans.email" +
|
||||
", projections.users11_humans.first_name" +
|
||||
", projections.users11_humans.last_name" +
|
||||
", projections.users11_humans.display_name" +
|
||||
", projections.users11_machines.name" +
|
||||
", projections.users11_humans.avatar_key" +
|
||||
", projections.users11.type" +
|
||||
", projections.users12_humans.email" +
|
||||
", projections.users12_humans.first_name" +
|
||||
", projections.users12_humans.last_name" +
|
||||
", projections.users12_humans.display_name" +
|
||||
", projections.users12_machines.name" +
|
||||
", projections.users12_humans.avatar_key" +
|
||||
", projections.users12.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.instance_members4 AS members " +
|
||||
"LEFT JOIN projections.users11_humans " +
|
||||
"ON members.user_id = projections.users11_humans.user_id AND members.instance_id = projections.users11_humans.instance_id " +
|
||||
"LEFT JOIN projections.users11_machines " +
|
||||
"ON members.user_id = projections.users11_machines.user_id AND members.instance_id = projections.users11_machines.instance_id " +
|
||||
"LEFT JOIN projections.users11 " +
|
||||
"ON members.user_id = projections.users11.id AND members.instance_id = projections.users11.instance_id " +
|
||||
"LEFT JOIN projections.users12_humans " +
|
||||
"ON members.user_id = projections.users12_humans.user_id AND members.instance_id = projections.users12_humans.instance_id " +
|
||||
"LEFT JOIN projections.users12_machines " +
|
||||
"ON members.user_id = projections.users12_machines.user_id AND members.instance_id = projections.users12_machines.instance_id " +
|
||||
"LEFT JOIN projections.users12 " +
|
||||
"ON members.user_id = projections.users12.id AND members.instance_id = projections.users12.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id AND members.instance_id = projections.login_names3.instance_id " +
|
||||
"AS OF SYSTEM TIME '-1 ms' " +
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
|
||||
"github.com/zitadel/zitadel/internal/query/projection"
|
||||
@ -30,11 +29,21 @@ func TriggerIntrospectionProjections(ctx context.Context) {
|
||||
triggerBatch(ctx, introspectionTriggerHandlers()...)
|
||||
}
|
||||
|
||||
type AppType string
|
||||
|
||||
const (
|
||||
AppTypeAPI = "api"
|
||||
AppTypeOIDC = "oidc"
|
||||
)
|
||||
|
||||
type IntrospectionClient struct {
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ProjectID string
|
||||
PublicKeys database.Map[[]byte]
|
||||
AppID string
|
||||
ClientID string
|
||||
HashedSecret string
|
||||
AppType AppType
|
||||
ProjectID string
|
||||
ResourceOwner string
|
||||
PublicKeys database.Map[[]byte]
|
||||
}
|
||||
|
||||
//go:embed embed/introspection_client_by_id.sql
|
||||
@ -50,7 +59,15 @@ func (q *Queries) GetIntrospectionClientByID(ctx context.Context, clientID strin
|
||||
)
|
||||
|
||||
err = q.client.QueryRowContext(ctx, func(row *sql.Row) error {
|
||||
return row.Scan(&client.ClientID, &client.ClientSecret, &client.ProjectID, &client.PublicKeys)
|
||||
return row.Scan(
|
||||
&client.AppID,
|
||||
&client.ClientID,
|
||||
&client.HashedSecret,
|
||||
&client.AppType,
|
||||
&client.ProjectID,
|
||||
&client.ResourceOwner,
|
||||
&client.PublicKeys,
|
||||
)
|
||||
},
|
||||
introspectionClientByIDQuery,
|
||||
instanceID, clientID, getKeys,
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
@ -12,20 +11,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
)
|
||||
|
||||
func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
||||
secret := &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeHash,
|
||||
Algorithm: "alg",
|
||||
KeyID: "keyID",
|
||||
Crypted: []byte("secret"),
|
||||
}
|
||||
encSecret, err := json.Marshal(secret)
|
||||
require.NoError(t, err)
|
||||
|
||||
pubkeys := database.Map[[]byte]{
|
||||
"key1": {1, 2, 3},
|
||||
"key2": {4, 5, 6},
|
||||
@ -61,14 +50,17 @@ func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
||||
getKeys: false,
|
||||
},
|
||||
mock: mockQuery(expQuery,
|
||||
[]string{"client_id", "client_secret", "project_id", "public_keys"},
|
||||
[]driver.Value{"clientID", encSecret, "projectID", nil},
|
||||
[]string{"app_id", "client_id", "client_secret", "app_type", "project_id", "resource_owner", "public_keys"},
|
||||
[]driver.Value{"appID", "clientID", "secret", "oidc", "projectID", "orgID", nil},
|
||||
"instanceID", "clientID", false),
|
||||
want: &IntrospectionClient{
|
||||
ClientID: "clientID",
|
||||
ClientSecret: secret,
|
||||
ProjectID: "projectID",
|
||||
PublicKeys: nil,
|
||||
AppID: "appID",
|
||||
ClientID: "clientID",
|
||||
HashedSecret: "secret",
|
||||
AppType: AppTypeOIDC,
|
||||
ProjectID: "projectID",
|
||||
ResourceOwner: "orgID",
|
||||
PublicKeys: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -78,14 +70,17 @@ func TestQueries_GetIntrospectionClientByID(t *testing.T) {
|
||||
getKeys: true,
|
||||
},
|
||||
mock: mockQuery(expQuery,
|
||||
[]string{"client_id", "client_secret", "project_id", "public_keys"},
|
||||
[]driver.Value{"clientID", nil, "projectID", encPubkeys},
|
||||
[]string{"app_id", "client_id", "client_secret", "app_type", "project_id", "resource_owner", "public_keys"},
|
||||
[]driver.Value{"appID", "clientID", "", "oidc", "projectID", "orgID", encPubkeys},
|
||||
"instanceID", "clientID", true),
|
||||
want: &IntrospectionClient{
|
||||
ClientID: "clientID",
|
||||
ClientSecret: nil,
|
||||
ProjectID: "projectID",
|
||||
PublicKeys: pubkeys,
|
||||
AppID: "appID",
|
||||
ClientID: "clientID",
|
||||
HashedSecret: "",
|
||||
AppType: AppTypeOIDC,
|
||||
ProjectID: "projectID",
|
||||
ResourceOwner: "orgID",
|
||||
PublicKeys: pubkeys,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
@ -20,7 +19,7 @@ type OIDCClient struct {
|
||||
AppID string `json:"app_id,omitempty"`
|
||||
State domain.AppState `json:"state,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"`
|
||||
HashedSecret string `json:"client_secret,omitempty"`
|
||||
RedirectURIs []string `json:"redirect_uris,omitempty"`
|
||||
ResponseTypes []domain.OIDCResponseType `json:"response_types,omitempty"`
|
||||
GrantTypes []domain.OIDCGrantType `json:"grant_types,omitempty"`
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@ -66,7 +65,7 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
||||
AppID: "236647088211886082",
|
||||
State: domain.AppStateActive,
|
||||
ClientID: "236647088211951618@tests",
|
||||
ClientSecret: nil,
|
||||
HashedSecret: "",
|
||||
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode, domain.OIDCGrantTypeRefreshToken},
|
||||
@ -97,7 +96,7 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
||||
AppID: "236646457053020162",
|
||||
State: domain.AppStateActive,
|
||||
ClientID: "236646457053085698@tests",
|
||||
ClientSecret: nil,
|
||||
HashedSecret: "",
|
||||
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
|
||||
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
|
||||
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
|
||||
@ -124,15 +123,11 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
||||
name: "secret client",
|
||||
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientSecret}, "instanceID", "clientID", true),
|
||||
want: &OIDCClient{
|
||||
InstanceID: "230690539048009730",
|
||||
AppID: "236646858984783874",
|
||||
State: domain.AppStateActive,
|
||||
ClientID: "236646858984849410@tests",
|
||||
ClientSecret: &crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeHash,
|
||||
Algorithm: "bcrypt",
|
||||
Crypted: []byte(`$2a$14$OzZ0XEZZEtD13py/EPba2evsS6WcKZ5orVMj9pWHEGEHmLu2h3PFq`),
|
||||
},
|
||||
InstanceID: "230690539048009730",
|
||||
AppID: "236646858984783874",
|
||||
State: domain.AppStateActive,
|
||||
ClientID: "236646858984849410@tests",
|
||||
HashedSecret: "$2a$14$OzZ0XEZZEtD13py/EPba2evsS6WcKZ5orVMj9pWHEGEHmLu2h3PFq",
|
||||
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
|
||||
ResponseTypes: []domain.OIDCResponseType{0},
|
||||
GrantTypes: []domain.OIDCGrantType{0},
|
||||
@ -163,7 +158,7 @@ low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
|
||||
AppID: "239520764276441090",
|
||||
State: domain.AppStateActive,
|
||||
ClientID: "239520764779364354@zitadel",
|
||||
ClientSecret: nil,
|
||||
HashedSecret: "",
|
||||
RedirectURIs: []string{
|
||||
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
|
||||
"http://test.localhost.com:9000/ui/console/auth/callback"},
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users11_humans.email" +
|
||||
", projections.users11_humans.first_name" +
|
||||
", projections.users11_humans.last_name" +
|
||||
", projections.users11_humans.display_name" +
|
||||
", projections.users11_machines.name" +
|
||||
", projections.users11_humans.avatar_key" +
|
||||
", projections.users11.type" +
|
||||
", projections.users12_humans.email" +
|
||||
", projections.users12_humans.first_name" +
|
||||
", projections.users12_humans.last_name" +
|
||||
", projections.users12_humans.display_name" +
|
||||
", projections.users12_machines.name" +
|
||||
", projections.users12_humans.avatar_key" +
|
||||
", projections.users12.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.org_members4 AS members " +
|
||||
"LEFT JOIN projections.users11_humans " +
|
||||
"ON members.user_id = projections.users11_humans.user_id " +
|
||||
"AND members.instance_id = projections.users11_humans.instance_id " +
|
||||
"LEFT JOIN projections.users11_machines " +
|
||||
"ON members.user_id = projections.users11_machines.user_id " +
|
||||
"AND members.instance_id = projections.users11_machines.instance_id " +
|
||||
"LEFT JOIN projections.users11 " +
|
||||
"ON members.user_id = projections.users11.id " +
|
||||
"AND members.instance_id = projections.users11.instance_id " +
|
||||
"LEFT JOIN projections.users12_humans " +
|
||||
"ON members.user_id = projections.users12_humans.user_id " +
|
||||
"AND members.instance_id = projections.users12_humans.instance_id " +
|
||||
"LEFT JOIN projections.users12_machines " +
|
||||
"ON members.user_id = projections.users12_machines.user_id " +
|
||||
"AND members.instance_id = projections.users12_machines.instance_id " +
|
||||
"LEFT JOIN projections.users12 " +
|
||||
"ON members.user_id = projections.users12.id " +
|
||||
"AND members.instance_id = projections.users12.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users11_humans.email" +
|
||||
", projections.users11_humans.first_name" +
|
||||
", projections.users11_humans.last_name" +
|
||||
", projections.users11_humans.display_name" +
|
||||
", projections.users11_machines.name" +
|
||||
", projections.users11_humans.avatar_key" +
|
||||
", projections.users11.type" +
|
||||
", projections.users12_humans.email" +
|
||||
", projections.users12_humans.first_name" +
|
||||
", projections.users12_humans.last_name" +
|
||||
", projections.users12_humans.display_name" +
|
||||
", projections.users12_machines.name" +
|
||||
", projections.users12_humans.avatar_key" +
|
||||
", projections.users12.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.project_grant_members4 AS members " +
|
||||
"LEFT JOIN projections.users11_humans " +
|
||||
"ON members.user_id = projections.users11_humans.user_id " +
|
||||
"AND members.instance_id = projections.users11_humans.instance_id " +
|
||||
"LEFT JOIN projections.users11_machines " +
|
||||
"ON members.user_id = projections.users11_machines.user_id " +
|
||||
"AND members.instance_id = projections.users11_machines.instance_id " +
|
||||
"LEFT JOIN projections.users11 " +
|
||||
"ON members.user_id = projections.users11.id " +
|
||||
"AND members.instance_id = projections.users11.instance_id " +
|
||||
"LEFT JOIN projections.users12_humans " +
|
||||
"ON members.user_id = projections.users12_humans.user_id " +
|
||||
"AND members.instance_id = projections.users12_humans.instance_id " +
|
||||
"LEFT JOIN projections.users12_machines " +
|
||||
"ON members.user_id = projections.users12_machines.user_id " +
|
||||
"AND members.instance_id = projections.users12_machines.instance_id " +
|
||||
"LEFT JOIN projections.users12 " +
|
||||
"ON members.user_id = projections.users12.id " +
|
||||
"AND members.instance_id = projections.users12.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -21,24 +21,24 @@ var (
|
||||
", members.user_id" +
|
||||
", members.roles" +
|
||||
", projections.login_names3.login_name" +
|
||||
", projections.users11_humans.email" +
|
||||
", projections.users11_humans.first_name" +
|
||||
", projections.users11_humans.last_name" +
|
||||
", projections.users11_humans.display_name" +
|
||||
", projections.users11_machines.name" +
|
||||
", projections.users11_humans.avatar_key" +
|
||||
", projections.users11.type" +
|
||||
", projections.users12_humans.email" +
|
||||
", projections.users12_humans.first_name" +
|
||||
", projections.users12_humans.last_name" +
|
||||
", projections.users12_humans.display_name" +
|
||||
", projections.users12_machines.name" +
|
||||
", projections.users12_humans.avatar_key" +
|
||||
", projections.users12.type" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM projections.project_members4 AS members " +
|
||||
"LEFT JOIN projections.users11_humans " +
|
||||
"ON members.user_id = projections.users11_humans.user_id " +
|
||||
"AND members.instance_id = projections.users11_humans.instance_id " +
|
||||
"LEFT JOIN projections.users11_machines " +
|
||||
"ON members.user_id = projections.users11_machines.user_id " +
|
||||
"AND members.instance_id = projections.users11_machines.instance_id " +
|
||||
"LEFT JOIN projections.users11 " +
|
||||
"ON members.user_id = projections.users11.id " +
|
||||
"AND members.instance_id = projections.users11.instance_id " +
|
||||
"LEFT JOIN projections.users12_humans " +
|
||||
"ON members.user_id = projections.users12_humans.user_id " +
|
||||
"AND members.instance_id = projections.users12_humans.instance_id " +
|
||||
"LEFT JOIN projections.users12_machines " +
|
||||
"ON members.user_id = projections.users12_machines.user_id " +
|
||||
"AND members.instance_id = projections.users12_machines.instance_id " +
|
||||
"LEFT JOIN projections.users12 " +
|
||||
"ON members.user_id = projections.users12.id " +
|
||||
"AND members.instance_id = projections.users12.instance_id " +
|
||||
"LEFT JOIN projections.login_names3 " +
|
||||
"ON members.user_id = projections.login_names3.user_id " +
|
||||
"AND members.instance_id = projections.login_names3.instance_id " +
|
||||
|
@ -3,6 +3,7 @@ package projection
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AppProjectionTable = "projections.apps6"
|
||||
AppProjectionTable = "projections.apps7"
|
||||
AppAPITable = AppProjectionTable + "_" + appAPITableSuffix
|
||||
AppOIDCTable = AppProjectionTable + "_" + appOIDCTableSuffix
|
||||
AppSAMLTable = AppProjectionTable + "_" + appSAMLTableSuffix
|
||||
@ -96,7 +97,7 @@ func (*appProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AppAPIConfigColumnAppID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppAPIConfigColumnInstanceID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppAPIConfigColumnClientID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppAPIConfigColumnClientSecret, handler.ColumnTypeJSONB, handler.Nullable()),
|
||||
handler.NewColumn(AppAPIConfigColumnClientSecret, handler.ColumnTypeText, handler.Nullable()),
|
||||
handler.NewColumn(AppAPIConfigColumnAuthMethod, handler.ColumnTypeEnum),
|
||||
},
|
||||
handler.NewPrimaryKey(AppAPIConfigColumnInstanceID, AppAPIConfigColumnAppID),
|
||||
@ -109,7 +110,7 @@ func (*appProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(AppOIDCConfigColumnInstanceID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppOIDCConfigColumnVersion, handler.ColumnTypeEnum),
|
||||
handler.NewColumn(AppOIDCConfigColumnClientID, handler.ColumnTypeText),
|
||||
handler.NewColumn(AppOIDCConfigColumnClientSecret, handler.ColumnTypeJSONB, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnClientSecret, handler.ColumnTypeText, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnRedirectUris, handler.ColumnTypeTextArray, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnResponseTypes, handler.ColumnTypeEnumArray, handler.Nullable()),
|
||||
handler.NewColumn(AppOIDCConfigColumnGrantTypes, handler.ColumnTypeEnumArray, handler.Nullable()),
|
||||
@ -186,6 +187,10 @@ func (p *appProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: project.APIConfigSecretChangedType,
|
||||
Reduce: p.reduceAPIConfigSecretChanged,
|
||||
},
|
||||
{
|
||||
Event: project.APIConfigSecretHashUpdatedType,
|
||||
Reduce: p.reduceAPIConfigSecretHashUpdated,
|
||||
},
|
||||
{
|
||||
Event: project.OIDCConfigAddedType,
|
||||
Reduce: p.reduceOIDCConfigAdded,
|
||||
@ -198,6 +203,10 @@ func (p *appProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: project.OIDCConfigSecretChangedType,
|
||||
Reduce: p.reduceOIDCConfigSecretChanged,
|
||||
},
|
||||
{
|
||||
Event: project.OIDCConfigSecretHashUpdatedType,
|
||||
Reduce: p.reduceOIDCConfigSecretHashUpdated,
|
||||
},
|
||||
{
|
||||
Event: project.SAMLConfigAddedType,
|
||||
Reduce: p.reduceSAMLConfigAdded,
|
||||
@ -350,7 +359,7 @@ func (p *appProjection) reduceAPIConfigAdded(event eventstore.Event) (*handler.S
|
||||
handler.NewCol(AppAPIConfigColumnAppID, e.AppID),
|
||||
handler.NewCol(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(AppAPIConfigColumnClientID, e.ClientID),
|
||||
handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret),
|
||||
handler.NewCol(AppAPIConfigColumnClientSecret, crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)),
|
||||
handler.NewCol(AppAPIConfigColumnAuthMethod, e.AuthMethodType),
|
||||
},
|
||||
handler.WithTableSuffix(appAPITableSuffix),
|
||||
@ -374,9 +383,6 @@ func (p *appProjection) reduceAPIConfigChanged(event eventstore.Event) (*handler
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-vnZKi", "reduce.wrong.event.type %s", project.APIConfigChangedType)
|
||||
}
|
||||
cols := make([]handler.Column, 0, 2)
|
||||
if e.ClientSecret != nil {
|
||||
cols = append(cols, handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret))
|
||||
}
|
||||
if e.AuthMethodType != nil {
|
||||
cols = append(cols, handler.NewCol(AppAPIConfigColumnAuthMethod, *e.AuthMethodType))
|
||||
}
|
||||
@ -415,7 +421,37 @@ func (p *appProjection) reduceAPIConfigSecretChanged(event eventstore.Event) (*h
|
||||
e,
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret),
|
||||
handler.NewCol(AppAPIConfigColumnClientSecret, crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppAPIConfigColumnAppID, e.AppID),
|
||||
handler.NewCond(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
handler.WithTableSuffix(appAPITableSuffix),
|
||||
),
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(AppColumnSequence, e.Sequence()),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppColumnID, e.AppID),
|
||||
handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *appProjection) reduceAPIConfigSecretHashUpdated(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*project.APIConfigSecretHashUpdatedEvent)
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-ttb0I", "reduce.wrong.event.type %s", project.APIConfigSecretHashUpdatedType)
|
||||
}
|
||||
return handler.NewMultiStatement(
|
||||
e,
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppAPIConfigColumnClientSecret, e.HashedSecret),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppAPIConfigColumnAppID, e.AppID),
|
||||
@ -449,7 +485,7 @@ func (p *appProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler.
|
||||
handler.NewCol(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID),
|
||||
handler.NewCol(AppOIDCConfigColumnVersion, e.Version),
|
||||
handler.NewCol(AppOIDCConfigColumnClientID, e.ClientID),
|
||||
handler.NewCol(AppOIDCConfigColumnClientSecret, e.ClientSecret),
|
||||
handler.NewCol(AppOIDCConfigColumnClientSecret, crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)),
|
||||
handler.NewCol(AppOIDCConfigColumnRedirectUris, database.TextArray[string](e.RedirectUris)),
|
||||
handler.NewCol(AppOIDCConfigColumnResponseTypes, database.NumberArray[domain.OIDCResponseType](e.ResponseTypes)),
|
||||
handler.NewCol(AppOIDCConfigColumnGrantTypes, database.NumberArray[domain.OIDCGrantType](e.GrantTypes)),
|
||||
@ -569,7 +605,37 @@ func (p *appProjection) reduceOIDCConfigSecretChanged(event eventstore.Event) (*
|
||||
e,
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppOIDCConfigColumnClientSecret, e.ClientSecret),
|
||||
handler.NewCol(AppOIDCConfigColumnClientSecret, crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppOIDCConfigColumnAppID, e.AppID),
|
||||
handler.NewCond(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
handler.WithTableSuffix(appOIDCTableSuffix),
|
||||
),
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppColumnChangeDate, e.CreationDate()),
|
||||
handler.NewCol(AppColumnSequence, e.Sequence()),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppColumnID, e.AppID),
|
||||
handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID),
|
||||
},
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *appProjection) reduceOIDCConfigSecretHashUpdated(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*project.OIDCConfigSecretHashUpdatedEvent)
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-toSh1", "reduce.wrong.event.type %s", project.OIDCConfigSecretHashUpdatedType)
|
||||
}
|
||||
return handler.NewMultiStatement(
|
||||
e,
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AppOIDCConfigColumnClientSecret, e.HashedSecret),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AppOIDCConfigColumnAppID, e.AppID),
|
||||
|
@ -46,7 +46,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps6 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.apps7 (id, name, project_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"my-app",
|
||||
@ -83,7 +83,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"my-app",
|
||||
anyArg{},
|
||||
@ -136,7 +136,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.AppStateInactive,
|
||||
anyArg{},
|
||||
@ -168,7 +168,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.AppStateActive,
|
||||
anyArg{},
|
||||
@ -200,7 +200,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps7 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
@ -227,7 +227,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (project_id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps7 WHERE (project_id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -254,7 +254,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.apps7 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
@ -264,7 +264,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceAPIConfigAdded",
|
||||
name: "project reduceAPIConfigAdded, v1 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
@ -273,7 +273,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"clientId": "client-id",
|
||||
"clientSecret": {},
|
||||
"clientSecret": {"CryptoType":1,"Algorithm":"bcrypt","Crypted":"c2VjcmV0"},
|
||||
"authMethodType": 1
|
||||
}`),
|
||||
), project.APIConfigAddedEventMapper),
|
||||
@ -285,17 +285,61 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps6_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedStmt: "INSERT INTO projections.apps7_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
"client-id",
|
||||
anyArg{},
|
||||
"secret",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceAPIConfigAdded, v2 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.APIConfigAddedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"clientId": "client-id",
|
||||
"hashedSecret": "secret",
|
||||
"authMethodType": 1
|
||||
}`),
|
||||
), project.APIConfigAddedEventMapper),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceAPIConfigAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps7_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
"client-id",
|
||||
"secret",
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -317,7 +361,6 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"clientId": "client-id",
|
||||
"clientSecret": {},
|
||||
"authMethodType": 1
|
||||
}`),
|
||||
), project.APIConfigChangedEventMapper),
|
||||
@ -329,16 +372,15 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7_api_configs SET auth_method = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
domain.APIAuthMethodTypePrivateKeyJWT,
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -372,16 +414,16 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceAPIConfigSecretChanged",
|
||||
name: "project reduceAPIConfigSecretChanged, v1 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.APIConfigSecretChangedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"client_secret": {}
|
||||
}`),
|
||||
"appId": "app-id",
|
||||
"clientSecret": {"CryptoType":1,"Algorithm":"bcrypt","Crypted":"c2VjcmV0"}
|
||||
}`),
|
||||
), project.APIConfigSecretChangedEventMapper),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceAPIConfigSecretChanged,
|
||||
@ -391,15 +433,15 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.apps7_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -412,7 +454,87 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigAdded",
|
||||
name: "project reduceAPIConfigSecretChanged, v2 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.APIConfigSecretChangedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"hashedSecret": "secret"
|
||||
}`),
|
||||
), project.APIConfigSecretChangedEventMapper),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceAPIConfigSecretChanged,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceAPIConfigSecretHashUpdated",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.APIConfigSecretHashUpdatedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"hashedSecret": "secret"
|
||||
}`),
|
||||
), eventstore.GenericEventMapper[project.APIConfigSecretHashUpdatedEvent]),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceAPIConfigSecretHashUpdated,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7_api_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigAdded, v1 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
@ -422,7 +544,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
"oidcVersion": 0,
|
||||
"appId": "app-id",
|
||||
"clientId": "client-id",
|
||||
"clientSecret": {},
|
||||
"clientSecret": {"CryptoType":1,"Algorithm":"bcrypt","Crypted":"c2VjcmV0"},
|
||||
"redirectUris": ["redirect.one.ch", "redirect.two.ch"],
|
||||
"responseTypes": [1,2],
|
||||
"grantTypes": [1,2],
|
||||
@ -447,13 +569,13 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps6_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
domain.OIDCVersionV1,
|
||||
"client-id",
|
||||
anyArg{},
|
||||
"secret",
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
database.NumberArray[domain.OIDCResponseType]{1, 2},
|
||||
database.NumberArray[domain.OIDCGrantType]{1, 2},
|
||||
@ -471,7 +593,79 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigAdded, v2 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.OIDCConfigAddedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"oidcVersion": 0,
|
||||
"appId": "app-id",
|
||||
"clientId": "client-id",
|
||||
"hashedSecret": "secret",
|
||||
"redirectUris": ["redirect.one.ch", "redirect.two.ch"],
|
||||
"responseTypes": [1,2],
|
||||
"grantTypes": [1,2],
|
||||
"applicationType": 2,
|
||||
"authMethodType": 2,
|
||||
"postLogoutRedirectUris": ["logout.one.ch", "logout.two.ch"],
|
||||
"devMode": true,
|
||||
"accessTokenType": 1,
|
||||
"accessTokenRoleAssertion": true,
|
||||
"idTokenRoleAssertion": true,
|
||||
"idTokenUserinfoAssertion": true,
|
||||
"clockSkew": 1000,
|
||||
"additionalOrigins": ["origin.one.ch", "origin.two.ch"],
|
||||
"skipNativeAppSuccessPage": true
|
||||
}`),
|
||||
), project.OIDCConfigAddedEventMapper),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceOIDCConfigAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.apps7_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)",
|
||||
expectedArgs: []interface{}{
|
||||
"app-id",
|
||||
"instance-id",
|
||||
domain.OIDCVersionV1,
|
||||
"client-id",
|
||||
"secret",
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
database.NumberArray[domain.OIDCResponseType]{1, 2},
|
||||
database.NumberArray[domain.OIDCGrantType]{1, 2},
|
||||
domain.OIDCApplicationTypeNative,
|
||||
domain.OIDCAuthMethodTypeNone,
|
||||
database.TextArray[string]{"logout.one.ch", "logout.two.ch"},
|
||||
true,
|
||||
domain.OIDCTokenTypeJWT,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
1 * time.Microsecond,
|
||||
database.TextArray[string]{"origin.one.ch", "origin.two.ch"},
|
||||
true,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -518,7 +712,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)",
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins, skip_native_app_success_page) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) WHERE (app_id = $16) AND (instance_id = $17)",
|
||||
expectedArgs: []interface{}{
|
||||
domain.OIDCVersionV1,
|
||||
database.TextArray[string]{"redirect.one.ch", "redirect.two.ch"},
|
||||
@ -540,7 +734,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -574,7 +768,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigSecretChanged",
|
||||
name: "project reduceOIDCConfigSecretChanged, v1 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
@ -582,7 +776,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"client_secret": {}
|
||||
"clientSecret": {"CryptoType":1,"Algorithm":"bcrypt","Crypted":"c2VjcmV0"}
|
||||
}`),
|
||||
), project.OIDCConfigSecretChangedEventMapper),
|
||||
},
|
||||
@ -593,15 +787,95 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps6 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigSecretChanged, v2 secret",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.OIDCConfigSecretChangedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"hashedSecret": "secret"
|
||||
}`),
|
||||
), project.OIDCConfigSecretChangedEventMapper),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceOIDCConfigSecretChanged,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project reduceOIDCConfigSecretHashUpdated",
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
project.OIDCConfigSecretHashUpdatedType,
|
||||
project.AggregateType,
|
||||
[]byte(`{
|
||||
"appId": "app-id",
|
||||
"hashedSecret": "secret"
|
||||
}`),
|
||||
), eventstore.GenericEventMapper[project.OIDCConfigSecretHashUpdatedEvent]),
|
||||
},
|
||||
reduce: (&appProjection{}).reduceOIDCConfigSecretHashUpdated,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("project"),
|
||||
sequence: 15,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7_oidc_configs SET client_secret = $1 WHERE (app_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"secret",
|
||||
"app-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.apps7 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -630,7 +904,7 @@ func TestAppProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.apps6 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedStmt: "DELETE FROM projections.apps7 WHERE (instance_id = $1) AND (resource_owner = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"instance-id",
|
||||
"agg-id",
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
old_handler "github.com/zitadel/zitadel/internal/eventstore/handler"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserTable = "projections.users11"
|
||||
UserTable = "projections.users12"
|
||||
UserHumanTable = UserTable + "_" + UserHumanSuffix
|
||||
UserMachineTable = UserTable + "_" + UserMachineSuffix
|
||||
UserNotifyTable = UserTable + "_" + UserNotifySuffix
|
||||
@ -125,7 +126,7 @@ func (*userProjection) Init() *old_handler.Check {
|
||||
handler.NewColumn(MachineUserInstanceIDCol, handler.ColumnTypeText),
|
||||
handler.NewColumn(MachineNameCol, handler.ColumnTypeText),
|
||||
handler.NewColumn(MachineDescriptionCol, handler.ColumnTypeText, handler.Nullable()),
|
||||
handler.NewColumn(MachineSecretCol, handler.ColumnTypeJSONB, handler.Nullable()),
|
||||
handler.NewColumn(MachineSecretCol, handler.ColumnTypeText, handler.Nullable()),
|
||||
handler.NewColumn(MachineAccessTokenTypeCol, handler.ColumnTypeEnum, handler.Default(0)),
|
||||
},
|
||||
handler.NewPrimaryKey(MachineUserInstanceIDCol, MachineUserIDCol),
|
||||
@ -285,6 +286,10 @@ func (p *userProjection) Reducers() []handler.AggregateReducer {
|
||||
Event: user.MachineSecretSetType,
|
||||
Reduce: p.reduceMachineSecretSet,
|
||||
},
|
||||
{
|
||||
Event: user.MachineSecretHashUpdatedType,
|
||||
Reduce: p.reduceMachineSecretHashUpdated,
|
||||
},
|
||||
{
|
||||
Event: user.MachineSecretRemovedType,
|
||||
Reduce: p.reduceMachineSecretRemoved,
|
||||
@ -354,7 +359,7 @@ func (p *userProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
||||
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||
handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""),
|
||||
handler.NewCol(NotifyPasswordSetCol, crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""),
|
||||
},
|
||||
handler.WithTableSuffix(UserNotifySuffix),
|
||||
),
|
||||
@ -403,7 +408,7 @@ func (p *userProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
||||
handler.NewCol(NotifyInstanceIDCol, e.Aggregate().InstanceID),
|
||||
handler.NewCol(NotifyLastEmailCol, e.EmailAddress),
|
||||
handler.NewCol(NotifyLastPhoneCol, &sql.NullString{String: string(e.PhoneNumber), Valid: e.PhoneNumber != ""}),
|
||||
handler.NewCol(NotifyPasswordSetCol, user.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""),
|
||||
handler.NewCol(NotifyPasswordSetCol, crypto.SecretOrEncodedHash(e.Secret, e.EncodedHash) != ""),
|
||||
},
|
||||
handler.WithTableSuffix(UserNotifySuffix),
|
||||
),
|
||||
@ -952,7 +957,37 @@ func (p *userProjection) reduceMachineSecretSet(event eventstore.Event) (*handle
|
||||
),
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(MachineSecretCol, e.ClientSecret),
|
||||
handler.NewCol(MachineSecretCol, crypto.SecretOrEncodedHash(e.ClientSecret, e.HashedSecret)),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(MachineUserIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(MachineUserInstanceIDCol, e.Aggregate().InstanceID),
|
||||
},
|
||||
handler.WithTableSuffix(UserMachineSuffix),
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *userProjection) reduceMachineSecretHashUpdated(event eventstore.Event) (*handler.Statement, error) {
|
||||
e, ok := event.(*user.MachineSecretHashUpdatedEvent)
|
||||
if !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-Wieng4u", "reduce.wrong.event.type %s", user.MachineSecretHashUpdatedType)
|
||||
}
|
||||
return handler.NewMultiStatement(
|
||||
e,
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(UserChangeDateCol, e.CreationDate()),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(UserIDCol, e.Aggregate().ID),
|
||||
handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID),
|
||||
},
|
||||
),
|
||||
handler.AddUpdateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(MachineSecretCol, e.HashedSecret),
|
||||
},
|
||||
[]handler.Condition{
|
||||
handler.NewCond(MachineUserIDCol, e.Aggregate().ID),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user