mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-04 21:32:15 +00:00
feat: add apis and application keys (#1327)
* feat: add apis and application keys * VerifyOIDCClientSecret * Update internal/v2/repository/project/api_config.go Co-authored-by: Fabi <[email protected]> * Update internal/v2/repository/project/key.go Co-authored-by: Fabi <[email protected]> * fix append ApplicationKeyWriteModel Co-authored-by: Fabi <[email protected]>
This commit is contained in:
@@ -34,14 +34,15 @@ type CommandSide struct {
|
||||
passwordVerificationCode crypto.Generator
|
||||
machineKeyAlg crypto.EncryptionAlgorithm
|
||||
machineKeySize int
|
||||
applicationKeySize int
|
||||
applicationSecretGenerator crypto.Generator
|
||||
domainVerificationAlg *crypto.AESCrypto
|
||||
domainVerificationGenerator crypto.Generator
|
||||
domainVerificationValidator func(domain, token, verifier string, checkType http.CheckType) error
|
||||
//TODO: remove global model, or move to domain
|
||||
multifactors global_model.Multifactors
|
||||
webauthn *webauthn_helper.WebAuthN
|
||||
|
||||
webauthn *webauthn_helper.WebAuthN
|
||||
keySize int
|
||||
keyAlgorithm crypto.EncryptionAlgorithm
|
||||
privateKeyLifetime time.Duration
|
||||
@@ -85,6 +86,7 @@ func StartCommandSide(config *Config) (repo *CommandSide, err error) {
|
||||
repo.userPasswordAlg = crypto.NewBCrypt(config.SystemDefaults.SecretGenerators.PasswordSaltCost)
|
||||
repo.machineKeyAlg = userEncryptionAlgorithm
|
||||
repo.machineKeySize = int(config.SystemDefaults.SecretGenerators.MachineKeySize)
|
||||
repo.applicationKeySize = int(config.SystemDefaults.SecretGenerators.ApplicationKeySize)
|
||||
|
||||
aesOTPCrypto, err := crypto.NewAESCrypto(config.SystemDefaults.Multifactors.OTP.VerificationKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/project"
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string) (_ *domain.APIApp, err error) {
|
||||
project, err := r.getProjectByID(ctx, application.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedApplication := NewAPIApplicationWriteModel(application.AggregateID, resourceOwner)
|
||||
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
|
||||
events, stringPw, err := r.addAPIApplication(ctx, projectAgg, project, application, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addedApplication.AppID = application.AppID
|
||||
pushedEvents, err := r.eventstore.PushEvents(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(addedApplication, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := apiWriteModelToAPIConfig(addedApplication)
|
||||
result.ClientSecretString = stringPw
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string) (events []eventstore.EventPusher, stringPW string, err error) {
|
||||
if !apiAppApp.IsValid() {
|
||||
return nil, "", caos_errs.ThrowPreconditionFailed(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
|
||||
}
|
||||
apiAppApp.AppID, err = r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
events = []eventstore.EventPusher{
|
||||
project.NewApplicationAddedEvent(ctx, projectAgg, apiAppApp.AppID, apiAppApp.AppName, resourceOwner),
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
err = domain.SetNewClientID(apiAppApp, r.idGenerator, proj)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(apiAppApp, r.applicationSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
events = append(events, project.NewAPIConfigAddedEvent(ctx,
|
||||
projectAgg,
|
||||
apiAppApp.AppID,
|
||||
apiAppApp.ClientID,
|
||||
apiAppApp.ClientSecret,
|
||||
apiAppApp.AuthMethodType))
|
||||
|
||||
return events, stringPw, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
|
||||
if !apiApp.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")
|
||||
}
|
||||
|
||||
existingAPI, err := r.getAPIAppWriteModel(ctx, apiApp.AggregateID, apiApp.AppID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingAPI.State == domain.AppStateUnspecified || existingAPI.State == domain.AppStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2n8uU", "Errors.Project.App.NotExisting")
|
||||
}
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingAPI.WriteModel)
|
||||
changedEvent, hasChanged, err := existingAPI.NewChangedEvent(
|
||||
ctx,
|
||||
projectAgg,
|
||||
apiApp.AppID,
|
||||
apiApp.AuthMethodType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-1m88i", "Errors.NoChangesFound")
|
||||
}
|
||||
|
||||
pushedEvents, err := r.eventstore.PushEvents(ctx, changedEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingAPI, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := apiWriteModelToAPIConfig(existingAPI)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.APIApp, error) {
|
||||
if projectID == "" || appID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-99i83", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingAPI, err := r.getAPIAppWriteModel(ctx, projectID, appID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingAPI.State == domain.AppStateUnspecified || existingAPI.State == domain.AppStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-2g66f", "Errors.Project.App.NotExisting")
|
||||
}
|
||||
cryptoSecret, stringPW, err := domain.NewClientSecret(r.applicationSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectAgg := ProjectAggregateFromWriteModel(&existingAPI.WriteModel)
|
||||
|
||||
pushedEvents, err := r.eventstore.PushEvents(ctx, project.NewAPIConfigSecretChangedEvent(ctx, projectAgg, appID, cryptoSecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingAPI, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := apiWriteModelToAPIConfig(existingAPI)
|
||||
result.ClientSecretString = stringPW
|
||||
return result, err
|
||||
}
|
||||
func (r *CommandSide) getAPIAppWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*APIApplicationWriteModel, error) {
|
||||
appWriteModel := NewAPIApplicationWriteModelWithAppID(projectID, appID, resourceOwner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, appWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return appWriteModel, nil
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/project"
|
||||
)
|
||||
|
||||
type APIApplicationWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AppID string
|
||||
AppName string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ClientSecretString string
|
||||
AuthMethodType domain.APIAuthMethodType
|
||||
State domain.AppState
|
||||
}
|
||||
|
||||
func NewAPIApplicationWriteModelWithAppID(projectID, appID, resourceOwner string) *APIApplicationWriteModel {
|
||||
return &APIApplicationWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: projectID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AppID: appID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAPIApplicationWriteModel(projectID, resourceOwner string) *APIApplicationWriteModel {
|
||||
return &APIApplicationWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: projectID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
func (wm *APIApplicationWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *project.ApplicationAddedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationChangedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationDeactivatedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationReactivatedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationRemovedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigAddedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigChangedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigSecretChangedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *project.ApplicationAddedEvent:
|
||||
wm.AppName = e.Name
|
||||
wm.State = domain.AppStateActive
|
||||
case *project.ApplicationChangedEvent:
|
||||
wm.AppName = e.Name
|
||||
case *project.ApplicationDeactivatedEvent:
|
||||
if wm.State == domain.AppStateRemoved {
|
||||
continue
|
||||
}
|
||||
wm.State = domain.AppStateInactive
|
||||
case *project.ApplicationReactivatedEvent:
|
||||
if wm.State == domain.AppStateRemoved {
|
||||
continue
|
||||
}
|
||||
wm.State = domain.AppStateActive
|
||||
case *project.ApplicationRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
case *project.APIConfigAddedEvent:
|
||||
wm.appendAddAPIEvent(e)
|
||||
case *project.APIConfigChangedEvent:
|
||||
wm.appendChangeAPIEvent(e)
|
||||
case *project.APIConfigSecretChangedEvent:
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) appendAddAPIEvent(e *project.APIConfigAddedEvent) {
|
||||
wm.ClientID = e.ClientID
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.AuthMethodType = e.AuthMethodType
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) appendChangeAPIEvent(e *project.APIConfigChangedEvent) {
|
||||
if e.AuthMethodType != nil {
|
||||
wm.AuthMethodType = *e.AuthMethodType
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
project.ApplicationAddedType,
|
||||
project.ApplicationChangedType,
|
||||
project.ApplicationDeactivatedType,
|
||||
project.ApplicationReactivatedType,
|
||||
project.ApplicationRemovedType,
|
||||
project.APIConfigAddedType,
|
||||
project.APIConfigChangedType,
|
||||
project.APIConfigSecretChangedType,
|
||||
project.ProjectRemovedType,
|
||||
)
|
||||
}
|
||||
|
||||
func (wm *APIApplicationWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
appID string,
|
||||
authMethodType domain.APIAuthMethodType,
|
||||
) (*project.APIConfigChangedEvent, bool, error) {
|
||||
changes := make([]project.APIConfigChanges, 0)
|
||||
var err error
|
||||
|
||||
if wm.AuthMethodType != authMethodType {
|
||||
changes = append(changes, project.ChangeAPIAuthMethodType(authMethodType))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
changeEvent, err := project.NewAPIConfigChangedEvent(ctx, aggregate, appID, changes)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return changeEvent, true, nil
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/project"
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddApplicationKey(ctx context.Context, key *domain.ApplicationKey, resourceOwner string) (_ *domain.ApplicationKey, err error) {
|
||||
application, err := r.getApplicationWriteModel(ctx, key.AggregateID, key.ApplicationID, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !application.State.Exists() {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "COMMAND-sak25", "Errors.Application.NotFound")
|
||||
}
|
||||
key.KeyID, err = r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyWriteModel := NewApplicationKeyWriteModel(key.AggregateID, key.ApplicationID, key.KeyID, resourceOwner)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, keyWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !keyWriteModel.KeysAllowed {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "COMMAND-Dff54", "Errors.Project.App.AuthMethodNoPrivateKeyJWT")
|
||||
}
|
||||
|
||||
if err := domain.EnsureValidExpirationDate(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = domain.SetNewAuthNKeyPair(key, r.applicationKeySize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key.ClientID = keyWriteModel.ClientID
|
||||
|
||||
pushedEvents, err := r.eventstore.PushEvents(ctx,
|
||||
project.NewApplicationKeyAddedEvent(
|
||||
ctx,
|
||||
ProjectAggregateFromWriteModel(&keyWriteModel.WriteModel),
|
||||
key.ApplicationID,
|
||||
key.ClientID,
|
||||
key.KeyID,
|
||||
key.Type,
|
||||
key.ExpirationDate,
|
||||
key.PublicKey),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(keyWriteModel, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := applicationKeyWriteModelToKey(keyWriteModel, key.PrivateKey)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveApplicationKey(ctx context.Context, projectID, applicationID, keyID, resourceOwner string) error {
|
||||
keyWriteModel := NewApplicationKeyWriteModel(projectID, applicationID, keyID, resourceOwner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, keyWriteModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !keyWriteModel.State.Exists() {
|
||||
return errors.ThrowNotFound(nil, "COMMAND-4m77G", "Errors.Application.Key.NotFound")
|
||||
}
|
||||
|
||||
_, err = r.eventstore.PushEvents(ctx, project.NewApplicationKeyRemovedEvent(ctx, ProjectAggregateFromWriteModel(&keyWriteModel.WriteModel), keyID))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/project"
|
||||
)
|
||||
|
||||
type ApplicationKeyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
AppID string
|
||||
ClientID string
|
||||
KeyID string
|
||||
KeyType domain.AuthNKeyType
|
||||
ExpirationDate time.Time
|
||||
|
||||
State domain.AppState
|
||||
KeysAllowed bool
|
||||
}
|
||||
|
||||
func NewApplicationKeyWriteModel(projectID, appID, keyID, resourceOwner string) *ApplicationKeyWriteModel {
|
||||
return &ApplicationKeyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: projectID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
AppID: appID,
|
||||
KeyID: keyID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *project.ApplicationRemovedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.OIDCConfigAddedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.OIDCConfigChangedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigAddedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.APIConfigChangedEvent:
|
||||
if e.AppID != wm.AppID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationKeyAddedEvent:
|
||||
if e.AppID != wm.AppID || e.KeyID != wm.KeyID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ApplicationKeyRemovedEvent:
|
||||
if e.KeyID != wm.KeyID {
|
||||
continue
|
||||
}
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.WriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *project.ApplicationRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
case *project.OIDCConfigAddedEvent:
|
||||
wm.appendAddOIDCEvent(e)
|
||||
case *project.OIDCConfigChangedEvent:
|
||||
wm.appendChangeOIDCEvent(e)
|
||||
case *project.APIConfigAddedEvent:
|
||||
wm.appendAddAPIEvent(e)
|
||||
case *project.APIConfigChangedEvent:
|
||||
wm.appendChangeAPIEvent(e)
|
||||
case *project.ApplicationKeyAddedEvent:
|
||||
wm.ClientID = e.ClientID
|
||||
wm.ExpirationDate = e.ExpirationDate
|
||||
wm.KeyType = e.KeyType
|
||||
case *project.ApplicationKeyRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
case *project.ProjectRemovedEvent:
|
||||
wm.State = domain.AppStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAddedEvent) {
|
||||
wm.ClientID = e.ClientID
|
||||
wm.KeysAllowed = e.AuthMethodType == domain.OIDCAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) appendChangeOIDCEvent(e *project.OIDCConfigChangedEvent) {
|
||||
if e.AuthMethodType != nil {
|
||||
wm.KeysAllowed = *e.AuthMethodType == domain.OIDCAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) appendAddAPIEvent(e *project.APIConfigAddedEvent) {
|
||||
wm.ClientID = e.ClientID
|
||||
wm.KeysAllowed = e.AuthMethodType == domain.APIAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) appendChangeAPIEvent(e *project.APIConfigChangedEvent) {
|
||||
if e.AuthMethodType != nil {
|
||||
wm.KeysAllowed = *e.AuthMethodType == domain.APIAuthMethodTypePrivateKeyJWT
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *ApplicationKeyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, project.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
EventTypes(
|
||||
project.ApplicationRemovedType,
|
||||
project.OIDCConfigAddedType,
|
||||
project.OIDCConfigChangedType,
|
||||
project.APIConfigAddedType,
|
||||
project.APIConfigChangedType,
|
||||
project.ApplicationKeyAddedEventType,
|
||||
project.ApplicationKeyRemovedEventType,
|
||||
project.ProjectRemovedType,
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,13 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/project"
|
||||
)
|
||||
@@ -48,11 +53,11 @@ func (r *CommandSide) addOIDCApplication(ctx context.Context, projectAgg *events
|
||||
}
|
||||
|
||||
var stringPw string
|
||||
err = oidcApp.GenerateNewClientID(r.idGenerator, proj)
|
||||
err = domain.SetNewClientID(oidcApp, r.idGenerator, proj)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
stringPw, err = oidcApp.GenerateClientSecretIfNeeded(r.applicationSecretGenerator)
|
||||
stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, r.applicationSecretGenerator)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -95,7 +100,6 @@ func (r *CommandSide) ChangeOIDCApplication(ctx context.Context, oidc *domain.OI
|
||||
ctx,
|
||||
projectAgg,
|
||||
oidc.AppID,
|
||||
oidc.ClientID,
|
||||
oidc.RedirectUris,
|
||||
oidc.PostLogoutRedirectUris,
|
||||
oidc.ResponseTypes,
|
||||
@@ -162,8 +166,34 @@ func (r *CommandSide) ChangeOIDCApplicationSecret(ctx context.Context, projectID
|
||||
result.ClientSecretString = stringPW
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (r *CommandSide) VerifyOIDCClientSecret(ctx context.Context, projectID, appID, secret string) error {
|
||||
app, err := r.getOIDCAppWriteModel(ctx, projectID, appID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !app.State.Exists() {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-D6hba", "Errors.Project.App.NoExisting")
|
||||
}
|
||||
if app.ClientSecret == nil {
|
||||
return caos_errs.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), r.userPasswordAlg)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
if err == nil {
|
||||
_, err = r.eventstore.PushEvents(ctx, project.NewOIDCConfigSecretCheckSucceededEvent(ctx, projectAgg, app.AppID))
|
||||
return err
|
||||
}
|
||||
_, err = r.eventstore.PushEvents(ctx, project.NewOIDCConfigSecretCheckFailedEvent(ctx, projectAgg, app.AppID))
|
||||
logging.Log("COMMAND-ADfhz").OnError(err).Error("could not push event OIDCClientSecretCheckFailed")
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-Bz542", "Errors.Project.App.OIDCSecretInvalid")
|
||||
}
|
||||
|
||||
func (r *CommandSide) getOIDCAppWriteModel(ctx context.Context, projectID, appID, resourceOwner string) (*OIDCApplicationWriteModel, error) {
|
||||
appWriteModel := NewOIDCApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner)
|
||||
appWriteModel := NewOIDCApplicationWriteModelWithAppID(projectID, appID, resourceOwner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, appWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -35,7 +35,7 @@ type OIDCApplicationWriteModel struct {
|
||||
State domain.AppState
|
||||
}
|
||||
|
||||
func NewOIDCApplicationWriteModelWithAppIDC(projectID, appID, resourceOwner string) *OIDCApplicationWriteModel {
|
||||
func NewOIDCApplicationWriteModelWithAppID(projectID, appID, resourceOwner string) *OIDCApplicationWriteModel {
|
||||
return &OIDCApplicationWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: projectID,
|
||||
@@ -154,9 +154,6 @@ func (wm *OIDCApplicationWriteModel) appendAddOIDCEvent(e *project.OIDCConfigAdd
|
||||
}
|
||||
|
||||
func (wm *OIDCApplicationWriteModel) appendChangeOIDCEvent(e *project.OIDCConfigChangedEvent) {
|
||||
if e.ClientID != nil {
|
||||
wm.ClientID = *e.ClientID
|
||||
}
|
||||
if e.RedirectUris != nil {
|
||||
wm.RedirectUris = *e.RedirectUris
|
||||
}
|
||||
@@ -218,8 +215,7 @@ func (wm *OIDCApplicationWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *OIDCApplicationWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
appID,
|
||||
clientID string,
|
||||
appID string,
|
||||
redirectURIS,
|
||||
postLogoutRedirectURIs []string,
|
||||
responseTypes []domain.OIDCResponseType,
|
||||
@@ -237,9 +233,6 @@ func (wm *OIDCApplicationWriteModel) NewChangedEvent(
|
||||
changes := make([]project.OIDCConfigChanges, 0)
|
||||
var err error
|
||||
|
||||
if wm.ClientID != clientID {
|
||||
changes = append(changes, project.ChangeClientID(clientID))
|
||||
}
|
||||
if !reflect.DeepEqual(wm.RedirectUris, redirectURIS) {
|
||||
changes = append(changes, project.ChangeRedirectURIs(redirectURIS))
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func applicationWriteModelToApplication(writeModel *ApplicationWriteModel) domai
|
||||
func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.OIDCApp {
|
||||
return &domain.OIDCApp{
|
||||
ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel),
|
||||
AppID: writeModel.AggregateID,
|
||||
AppID: writeModel.AppID,
|
||||
AppName: writeModel.AppName,
|
||||
State: writeModel.State,
|
||||
ClientID: writeModel.ClientID,
|
||||
@@ -55,6 +55,18 @@ func oidcWriteModelToOIDCConfig(writeModel *OIDCApplicationWriteModel) *domain.O
|
||||
}
|
||||
}
|
||||
|
||||
func apiWriteModelToAPIConfig(writeModel *APIApplicationWriteModel) *domain.APIApp {
|
||||
return &domain.APIApp{
|
||||
ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel),
|
||||
AppID: writeModel.AppID,
|
||||
AppName: writeModel.AppName,
|
||||
State: writeModel.State,
|
||||
ClientID: writeModel.ClientID,
|
||||
ClientSecret: writeModel.ClientSecret,
|
||||
AuthMethodType: writeModel.AuthMethodType,
|
||||
}
|
||||
}
|
||||
|
||||
func roleWriteModelToRole(writeModel *ProjectRoleWriteModel) *domain.ProjectRole {
|
||||
return &domain.ProjectRole{
|
||||
ObjectRoot: writeModelToObjectRoot(writeModel.WriteModel),
|
||||
@@ -72,3 +84,15 @@ func memberWriteModelToProjectGrantMember(writeModel *ProjectGrantMemberWriteMod
|
||||
UserID: writeModel.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
func applicationKeyWriteModelToKey(wm *ApplicationKeyWriteModel, privateKey []byte) *domain.ApplicationKey {
|
||||
return &domain.ApplicationKey{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
ApplicationID: wm.AppID,
|
||||
ClientID: wm.ClientID,
|
||||
KeyID: wm.KeyID,
|
||||
Type: wm.KeyType,
|
||||
ExpirationDate: wm.ExpirationDate,
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
@@ -10,11 +9,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
var (
|
||||
//most of us won't survive until 12-31-9999 23:59:59, maybe ZITADEL does
|
||||
defaultExpDate = time.Date(9999, time.December, 31, 23, 59, 59, 0, time.UTC)
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddUserMachineKey(ctx context.Context, machineKey *domain.MachineKey, resourceOwner string) (*domain.MachineKey, error) {
|
||||
err := r.checkUserExists(ctx, machineKey.AggregateID, resourceOwner)
|
||||
if err != nil {
|
||||
@@ -30,15 +24,11 @@ func (r *CommandSide) AddUserMachineKey(ctx context.Context, machineKey *domain.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if machineKey.ExpirationDate.IsZero() {
|
||||
machineKey.ExpirationDate = defaultExpDate
|
||||
}
|
||||
if machineKey.ExpirationDate.Before(time.Now()) {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-38vns", "Errors.MachineKey.ExpireBeforeNow")
|
||||
if err = domain.EnsureValidExpirationDate(machineKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = machineKey.GenerateNewMachineKeyPair(r.machineKeySize)
|
||||
if err != nil {
|
||||
if err = domain.SetNewAuthNKeyPair(machineKey, r.machineKeySize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type MachineKeyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
KeyID string
|
||||
KeyType domain.MachineKeyType
|
||||
KeyType domain.AuthNKeyType
|
||||
ExpirationDate time.Time
|
||||
|
||||
State domain.MachineKeyState
|
||||
|
||||
Reference in New Issue
Block a user