feat: V2 alpha import and export of organizations (#3798)

* feat(import): add functionality to import data into an instance

* feat(import): move import to admin api and additional checks for nil pointer

* fix(export): export implementation with filtered members and grants

* fix: export and import implementation

* fix: add possibility to export hashed passwords with the user

* fix(import): import with structure of v1 and v2

* docs: add v1 proto

* fix(import): check im imported user is already existing

* fix(import): add otp import function

* fix(import): add external idps, domains, custom text and messages

* fix(import): correct usage of default values from login policy

* fix(export): fix renaming of add project function

* fix(import): move checks for unit tests

* expect filter

* fix(import): move checks for unit tests

* fix(import): move checks for unit tests

* fix(import): produce prerelease from branch

* fix(import): correctly use provided user id for machine user imports

* fix(import): corrected otp import and added guide for export and import

* fix: import verified and primary domains

* fix(import): add reading from gcs, s3 and localfile with tracing

* fix(import): gcs and s3, file size correction and error logging

* Delete docker-compose.yml

* fix(import): progress logging and count of resources

* fix(import): progress logging and count of resources

* log subscription

* fix(import): incorporate review

* fix(import): incorporate review

* docs: add suggestion for import

Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>

* fix(import): add verification otp event and handling of deleted but existing users

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: Fabienne <fabienne.gerschwiler@gmail.com>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
Stefan Benz
2022-07-28 15:42:35 +02:00
committed by GitHub
parent d620126aab
commit bc9a85daf3
51 changed files with 4430 additions and 648 deletions

View File

@@ -21,17 +21,19 @@ type OrgSetup struct {
Roles []string
}
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string) (string, *domain.ObjectDetails, error) {
orgID, err := c.idGenerator.Next()
func (c *Commands) SetUpOrgWithIDs(ctx context.Context, o *OrgSetup, orgID, userID string, userIDs ...string) (string, *domain.ObjectDetails, error) {
existingOrg, err := c.getOrgWriteModelByID(ctx, orgID)
if err != nil {
return "", nil, err
}
userID, err := c.idGenerator.Next()
if err != nil {
return "", nil, err
if existingOrg != nil {
return "", nil, errors.ThrowPreconditionFailed(nil, "COMMAND-poaj2", "Errors.Org.AlreadyExisting")
}
return c.setUpOrgWithIDs(ctx, o, orgID, userID, userIDs...)
}
func (c *Commands) setUpOrgWithIDs(ctx context.Context, o *OrgSetup, orgID, userID string, userIDs ...string) (string, *domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(orgID)
userAgg := user_repo.NewAggregate(userID, orgID)
@@ -65,6 +67,20 @@ func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string)
}, nil
}
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, userIDs ...string) (string, *domain.ObjectDetails, error) {
orgID, err := c.idGenerator.Next()
if err != nil {
return "", nil, err
}
userID, err := c.idGenerator.Next()
if err != nil {
return "", nil, err
}
return c.setUpOrgWithIDs(ctx, o, orgID, userID, userIDs...)
}
//AddOrgCommand defines the commands to create a new org,
// this includes the verified default domain
func AddOrgCommand(ctx context.Context, a *org.Aggregate, name string, userIDs ...string) preparation.Validation {
@@ -106,8 +122,33 @@ func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
return nil
}
func (c *Commands) AddOrgWithID(ctx context.Context, name, userID, resourceOwner, orgID string, claimedUserIDs []string) (*domain.Org, error) {
existingOrg, err := c.getOrgWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingOrg.State != domain.OrgStateUnspecified {
return nil, caos_errs.ThrowNotFound(nil, "ORG-lapo2m", "Errors.Org.AlreadyExisting")
}
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, claimedUserIDs)
}
func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner string, claimedUserIDs []string) (*domain.Org, error) {
orgAgg, addedOrg, events, err := c.addOrg(ctx, &domain.Org{Name: name}, claimedUserIDs)
if name == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
}
orgID, err := c.idGenerator.Next()
if err != nil {
return nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
}
return c.addOrgWithIDAndMember(ctx, name, userID, resourceOwner, orgID, claimedUserIDs)
}
func (c *Commands) addOrgWithIDAndMember(ctx context.Context, name, userID, resourceOwner, orgID string, claimedUserIDs []string) (*domain.Org, error) {
orgAgg, addedOrg, events, err := c.addOrgWithID(ctx, &domain.Org{Name: name}, orgID, claimedUserIDs)
if err != nil {
return nil, err
}
@@ -136,6 +177,7 @@ func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.O
if orgID == "" || name == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
@@ -242,15 +284,12 @@ func ExistsOrg(ctx context.Context, filter preparation.FilterToQueryReducer, id
return exists, nil
}
func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
func (c *Commands) addOrgWithID(ctx context.Context, organisation *domain.Org, orgID string, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.Command, err error) {
if !organisation.IsValid() {
return nil, nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMM-deLSk", "Errors.Org.Invalid")
}
organisation.AggregateID, err = c.idGenerator.Next()
if err != nil {
return nil, nil, nil, caos_errs.ThrowInternal(err, "COMMA-OwciI", "Errors.Internal")
}
organisation.AggregateID = orgID
organisation.AddIAMDomain(authz.GetInstance(ctx).RequestedDomain())
addedOrg := NewOrgWriteModel(organisation.AggregateID)

View File

@@ -11,14 +11,33 @@ import (
"github.com/zitadel/zitadel/internal/repository/org"
)
func (c *Commands) AddActionWithID(ctx context.Context, addAction *domain.Action, resourceOwner, actionID string) (_ string, _ *domain.ObjectDetails, err error) {
existingAction, err := c.getActionWriteModelByID(ctx, actionID, resourceOwner)
if err != nil {
return "", nil, err
}
if existingAction.State != domain.ActionStateUnspecified {
return "", nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-nau2k", "Errors.Action.AlreadyExisting")
}
return c.addActionWithID(ctx, addAction, resourceOwner, actionID)
}
func (c *Commands) AddAction(ctx context.Context, addAction *domain.Action, resourceOwner string) (_ string, _ *domain.ObjectDetails, err error) {
if !addAction.IsValid() {
return "", nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-eg2gf", "Errors.Action.Invalid")
}
addAction.AggregateID, err = c.idGenerator.Next()
actionID, err := c.idGenerator.Next()
if err != nil {
return "", nil, err
}
return c.addActionWithID(ctx, addAction, resourceOwner, actionID)
}
func (c *Commands) addActionWithID(ctx context.Context, addAction *domain.Action, resourceOwner, actionID string) (_ string, _ *domain.ObjectDetails, err error) {
addAction.AggregateID = actionID
actionModel := NewActionWriteModel(addAction.AggregateID, resourceOwner)
actionAgg := ActionAggregateFromWriteModel(&actionModel.WriteModel)

View File

@@ -51,7 +51,7 @@ func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userI
}
}
func VerifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
func verifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if domain = strings.TrimSpace(domain); domain == "" {
return nil, errors.ThrowInvalidArgument(nil, "ORG-yqlVQ", "Errors.Invalid.Argument")
@@ -63,7 +63,7 @@ func VerifyOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
}
}
func SetPrimaryOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
func setPrimaryOrgDomain(a *org.Aggregate, domain string) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if domain = strings.TrimSpace(domain); domain == "" {
return nil, errors.ThrowInvalidArgument(nil, "ORG-gmNqY", "Errors.Invalid.Argument")
@@ -101,6 +101,19 @@ func orgDomain(ctx context.Context, filter preparation.FilterToQueryReducer, org
return wm, nil
}
func (c *Commands) VerifyOrgDomain(ctx context.Context, orgID, domain string) (*domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(orgID)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, verifyOrgDomain(orgAgg, domain))
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) AddOrgDomain(ctx context.Context, orgID, domain string, claimedUserIDs []string) (*domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(orgID)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgDomain(orgAgg, domain, claimedUserIDs))

View File

@@ -178,7 +178,7 @@ func TestVerifyDomain(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
AssertValidation(t, context.Background(), VerifyOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
AssertValidation(t, context.Background(), verifyOrgDomain(tt.args.a, tt.args.domain), nil, tt.want)
})
}
}
@@ -273,7 +273,7 @@ func TestSetDomainPrimary(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
AssertValidation(t, context.Background(), SetPrimaryOrgDomain(tt.args.a, tt.args.domain), tt.args.filter, tt.want)
AssertValidation(t, context.Background(), setPrimaryOrgDomain(tt.args.a, tt.args.domain), tt.args.filter, tt.want)
})
}
}

View File

@@ -11,6 +11,17 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) ImportIDPConfig(ctx context.Context, config *domain.IDPConfig, idpConfigID, resourceOwner string) (*domain.IDPConfig, error) {
existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, idpConfigID, resourceOwner)
if err != nil {
return nil, err
}
if existingIDP.State != domain.IDPConfigStateRemoved && existingIDP.State != domain.IDPConfigStateUnspecified {
return nil, errors.ThrowNotFound(nil, "Org-1J8fs", "Errors.Org.IDPConfig.AlreadyExisting")
}
return c.addIDPConfig(ctx, config, idpConfigID, resourceOwner)
}
func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, resourceOwner string) (*domain.IDPConfig, error) {
if resourceOwner == "" {
return nil, errors.ThrowInvalidArgument(nil, "Org-0j8gs", "Errors.ResourceOwnerMissing")
@@ -18,11 +29,16 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, r
if config.OIDCConfig == nil && config.JWTConfig == nil {
return nil, errors.ThrowInvalidArgument(nil, "Org-eUpQU", "Errors.idp.config.notset")
}
idpConfigID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
return c.addIDPConfig(ctx, config, idpConfigID, resourceOwner)
}
func (c *Commands) addIDPConfig(ctx context.Context, config *domain.IDPConfig, idpConfigID, resourceOwner string) (*domain.IDPConfig, error) {
addedConfig := NewOrgIDPConfigWriteModel(idpConfigID, resourceOwner)
orgAgg := OrgAggregateFromWriteModel(&addedConfig.WriteModel)

View File

@@ -13,11 +13,46 @@ import (
"github.com/zitadel/zitadel/internal/repository/project"
)
func (c *Commands) AddProject(ctx context.Context, project *domain.Project, resourceOwner, ownerUserID string) (_ *domain.Project, err error) {
events, addedProject, err := c.addProject(ctx, project, resourceOwner, ownerUserID)
func (c *Commands) AddProjectWithID(ctx context.Context, project *domain.Project, resourceOwner, projectID string) (_ *domain.Project, err error) {
existingProject, err := c.getProjectWriteModelByID(ctx, projectID, resourceOwner)
if err != nil {
return nil, err
}
if existingProject.State != domain.ProjectStateUnspecified {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-opamwu", "Errors.Project.AlreadyExisting")
}
return c.addProjectWithID(ctx, project, resourceOwner, projectID)
}
func (c *Commands) AddProject(ctx context.Context, project *domain.Project, resourceOwner, ownerUserID string) (_ *domain.Project, err error) {
if !project.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
}
projectID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
return c.addProjectWithIDWithOwner(ctx, project, resourceOwner, ownerUserID, projectID)
}
func (c *Commands) addProjectWithID(ctx context.Context, projectAdd *domain.Project, resourceOwner, projectID string) (_ *domain.Project, err error) {
projectAdd.AggregateID = projectID
addedProject := NewProjectWriteModel(projectAdd.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedProject.WriteModel)
events := []eventstore.Command{
project.NewProjectAddedEvent(
ctx,
projectAgg,
projectAdd.Name,
projectAdd.ProjectRoleAssertion,
projectAdd.ProjectRoleCheck,
projectAdd.HasProjectCheck,
projectAdd.PrivateLabelingSetting),
}
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
@@ -29,14 +64,11 @@ func (c *Commands) AddProject(ctx context.Context, project *domain.Project, reso
return projectWriteModelToProject(addedProject), nil
}
func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, resourceOwner, ownerUserID string) (_ []eventstore.Command, _ *ProjectWriteModel, err error) {
func (c *Commands) addProjectWithIDWithOwner(ctx context.Context, projectAdd *domain.Project, resourceOwner, ownerUserID, projectID string) (_ *domain.Project, err error) {
if !projectAdd.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
}
projectAdd.AggregateID, err = c.idGenerator.Next()
if err != nil {
return nil, nil, err
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-IOVCC", "Errors.Project.Invalid")
}
projectAdd.AggregateID = projectID
addedProject := NewProjectWriteModel(projectAdd.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedProject.WriteModel)
@@ -52,7 +84,16 @@ func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, r
projectAdd.PrivateLabelingSetting),
project.NewProjectMemberAddedEvent(ctx, projectAgg, ownerUserID, projectRole),
}
return events, addedProject, nil
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(addedProject, pushedEvents...)
if err != nil {
return nil, err
}
return projectWriteModelToProject(addedProject), nil
}
func AddProjectCommand(

View File

@@ -70,21 +70,70 @@ func (c *Commands) AddAPIAppCommand(app *addAPIApp, clientSecretAlg crypto.HashA
}
}
func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
if application == nil || application.AggregateID == "" {
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
}
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
if err != nil {
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsf", "Errors.Project.NotFound")
}
addedApplication := NewAPIApplicationWriteModel(application.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
events, stringPw, err := c.addAPIApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
func (c *Commands) AddAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
existingAPI, err := c.getAPIAppWriteModel(ctx, apiApp.AggregateID, appID, resourceOwner)
if err != nil {
return nil, err
}
addedApplication.AppID = application.AppID
if existingAPI.State != domain.AppStateUnspecified {
return nil, errors.ThrowPreconditionFailed(nil, "PROJECT-mabu12", "Errors.Application.AlreadyExisting")
}
project, err := c.getProjectByID(ctx, apiApp.AggregateID, resourceOwner)
if err != nil {
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsa", "Errors.Project.NotFound")
}
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
}
func (c *Commands) AddAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
if apiApp == nil || apiApp.AggregateID == "" {
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
}
project, err := c.getProjectByID(ctx, apiApp.AggregateID, resourceOwner)
if err != nil {
return nil, errors.ThrowPreconditionFailed(err, "PROJECT-9fnsf", "Errors.Project.NotFound")
}
if !apiApp.IsValid() {
return nil, errors.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
}
appID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
return c.addAPIApplicationWithID(ctx, apiApp, resourceOwner, project, appID, appSecretGenerator)
}
func (c *Commands) addAPIApplicationWithID(ctx context.Context, apiApp *domain.APIApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
apiApp.AppID = appID
addedApplication := NewAPIApplicationWriteModel(apiApp.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
events := []eventstore.Command{
project_repo.NewApplicationAddedEvent(ctx, projectAgg, apiApp.AppID, apiApp.AppName),
}
var stringPw string
err = domain.SetNewClientID(apiApp, c.idGenerator, project)
if err != nil {
return nil, err
}
stringPw, err = domain.SetNewClientSecretIfNeeded(apiApp, appSecretGenerator)
if err != nil {
return nil, err
}
events = append(events, project_repo.NewAPIConfigAddedEvent(ctx,
projectAgg,
apiApp.AppID,
apiApp.ClientID,
apiApp.ClientSecret,
apiApp.AuthMethodType))
addedApplication.AppID = apiApp.AppID
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
@@ -98,38 +147,6 @@ func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.AP
return result, nil
}
func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
if !apiAppApp.IsValid() {
return nil, "", errors.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
}
apiAppApp.AppID, err = c.idGenerator.Next()
if err != nil {
return nil, "", err
}
events = []eventstore.Command{
project_repo.NewApplicationAddedEvent(ctx, projectAgg, apiAppApp.AppID, apiAppApp.AppName),
}
var stringPw string
err = domain.SetNewClientID(apiAppApp, c.idGenerator, proj)
if err != nil {
return nil, "", err
}
stringPw, err = domain.SetNewClientSecretIfNeeded(apiAppApp, appSecretGenerator)
if err != nil {
return nil, "", err
}
events = append(events, project_repo.NewAPIConfigAddedEvent(ctx,
projectAgg,
apiAppApp.AppID,
apiAppApp.ClientID,
apiAppApp.ClientSecret,
apiAppApp.AuthMethodType))
return events, stringPw, nil
}
func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIApp, resourceOwner string) (*domain.APIApp, error) {
if apiApp.AppID == "" || apiApp.AggregateID == "" {
return nil, errors.ThrowInvalidArgument(nil, "COMMAND-1m900", "Errors.Project.App.APIConfigInvalid")

View File

@@ -116,56 +116,63 @@ func (c *Commands) AddOIDCAppCommand(app *addOIDCApp, clientSecretAlg crypto.Has
}
}
func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
if application == nil || application.AggregateID == "" {
func (c *Commands) AddOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
existingApp, err := c.getOIDCAppWriteModel(ctx, oidcApp.AggregateID, appID, resourceOwner)
if err != nil {
return nil, err
}
if existingApp.State != domain.AppStateUnspecified {
return nil, caos_errs.ThrowPreconditionFailed(nil, "PROJECT-lxowmp", "Errors.Application.AlreadyExisting")
}
project, err := c.getProjectByID(ctx, oidcApp.AggregateID, resourceOwner)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-3m9s2", "Errors.Project.NotFound")
}
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
}
func (c *Commands) AddOIDCApplication(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
if oidcApp == nil || oidcApp.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-34Fm0", "Errors.Application.Invalid")
}
project, err := c.getProjectByID(ctx, application.AggregateID, resourceOwner)
project, err := c.getProjectByID(ctx, oidcApp.AggregateID, resourceOwner)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "PROJECT-3m9ss", "Errors.Project.NotFound")
}
addedApplication := NewOIDCApplicationWriteModel(application.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
events, stringPw, err := c.addOIDCApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
if oidcApp.AppName == "" || !oidcApp.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-1n8df", "Errors.Application.Invalid")
}
appID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
addedApplication.AppID = application.AppID
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(addedApplication, pushedEvents...)
if err != nil {
return nil, err
}
result := oidcWriteModelToOIDCConfig(addedApplication)
result.ClientSecretString = stringPw
result.FillCompliance()
return result, nil
return c.addOIDCApplicationWithID(ctx, oidcApp, resourceOwner, project, appID, appSecretGenerator)
}
func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
if oidcApp.AppName == "" || !oidcApp.IsValid() {
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-1n8df", "Errors.Application.Invalid")
}
oidcApp.AppID, err = c.idGenerator.Next()
if err != nil {
return nil, "", err
}
func (c *Commands) addOIDCApplicationWithID(ctx context.Context, oidcApp *domain.OIDCApp, resourceOwner string, project *domain.Project, appID string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
events = []eventstore.Command{
addedApplication := NewOIDCApplicationWriteModel(oidcApp.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
oidcApp.AppID = appID
events := []eventstore.Command{
project_repo.NewApplicationAddedEvent(ctx, projectAgg, oidcApp.AppID, oidcApp.AppName),
}
var stringPw string
err = domain.SetNewClientID(oidcApp, c.idGenerator, proj)
err = domain.SetNewClientID(oidcApp, c.idGenerator, project)
if err != nil {
return nil, "", err
return nil, err
}
stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, appSecretGenerator)
if err != nil {
return nil, "", err
return nil, err
}
events = append(events, project_repo.NewOIDCConfigAddedEvent(ctx,
projectAgg,
@@ -187,7 +194,19 @@ func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstor
oidcApp.ClockSkew,
oidcApp.AdditionalOrigins))
return events, stringPw, nil
addedApplication.AppID = oidcApp.AppID
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(addedApplication, pushedEvents...)
if err != nil {
return nil, err
}
result := oidcWriteModelToOIDCConfig(addedApplication)
result.ClientSecretString = stringPw
result.FillCompliance()
return result, nil
}
func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCApp, resourceOwner string) (*domain.OIDCApp, error) {

View File

@@ -12,6 +12,18 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddProjectGrantWithID(ctx context.Context, grant *domain.ProjectGrant, grantID string, resourceOwner string) (_ *domain.ProjectGrant, err error) {
existingMember, err := c.projectGrantWriteModelByID(ctx, grantID, grant.AggregateID, resourceOwner)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if existingMember != nil && existingMember.State != domain.ProjectGrantStateUnspecified {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-2b8fs", "Errors.Project.Grant.AlreadyExisting")
}
return c.addProjectGrantWithID(ctx, grant, grantID, resourceOwner)
}
func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGrant, resourceOwner string) (_ *domain.ProjectGrant, err error) {
if !grant.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-3b8fs", "Errors.Project.Grant.Invalid")
@@ -20,10 +32,18 @@ func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGra
if err != nil {
return nil, err
}
grant.GrantID, err = c.idGenerator.Next()
grantID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
return c.addProjectGrantWithID(ctx, grant, grantID, resourceOwner)
}
func (c *Commands) addProjectGrantWithID(ctx context.Context, grant *domain.ProjectGrant, grantID string, resourceOwner string) (_ *domain.ProjectGrant, err error) {
grant.GrantID = grantID
addedGrant := NewProjectGrantWriteModel(grant.GrantID, grant.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedGrant.WriteModel)
pushedEvents, err := c.eventstore.Push(

View File

@@ -47,6 +47,8 @@ type AddHuman struct {
Phone Phone
//Password is optional
Password string
//BcryptedPassword is optional
BcryptedPassword string
//PasswordChangeRequired is used if the `Password`-field is set
PasswordChangeRequired bool
Passwordless bool
@@ -54,14 +56,19 @@ type AddHuman struct {
Register bool
}
func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *AddHuman) (*domain.HumanDetails, error) {
if resourceOwner == "" {
return nil, errors.ThrowInvalidArgument(nil, "COMMA-5Ky74", "Errors.Internal")
}
userID, err := c.idGenerator.Next()
func (c *Commands) AddHumanWithID(ctx context.Context, resourceOwner string, userID string, human *AddHuman) (*domain.HumanDetails, error) {
existingHuman, err := c.getHumanWriteModelByID(ctx, userID, resourceOwner)
if err != nil {
return nil, err
}
if isUserStateExists(existingHuman.UserState) {
return nil, errors.ThrowPreconditionFailed(nil, "COMMAND-k2unb", "Errors.User.AlreadyExisting")
}
return c.addHumanWithID(ctx, resourceOwner, userID, human)
}
func (c *Commands) addHumanWithID(ctx context.Context, resourceOwner string, userID string, human *AddHuman) (*domain.HumanDetails, error) {
agg := user.NewAggregate(userID, resourceOwner)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, AddHumanCommand(agg, human, c.userPasswordAlg, c.userEncryption))
if err != nil {
@@ -83,6 +90,18 @@ func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *Ad
}, nil
}
func (c *Commands) AddHuman(ctx context.Context, resourceOwner string, human *AddHuman) (*domain.HumanDetails, error) {
if resourceOwner == "" {
return nil, errors.ThrowInvalidArgument(nil, "COMMA-5Ky74", "Errors.Internal")
}
userID, err := c.idGenerator.Next()
if err != nil {
return nil, err
}
return c.addHumanWithID(ctx, resourceOwner, userID, human)
}
type humanCreationCommand interface {
eventstore.Command
AddPhoneData(phoneNumber string)
@@ -167,6 +186,10 @@ func AddHumanCommand(a *user.Aggregate, human *AddHuman, passwordAlg crypto.Hash
createCmd.AddPasswordData(secret, human.PasswordChangeRequired)
}
if human.BcryptedPassword != "" {
createCmd.AddPasswordData(crypto.FillHash([]byte(human.BcryptedPassword), passwordAlg), human.PasswordChangeRequired)
}
cmds := make([]eventstore.Command, 0, 3)
cmds = append(cmds, createCmd)
@@ -274,6 +297,18 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
if err != nil {
return nil, nil, errors.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexityPolicy.NotFound")
}
if human.AggregateID != "" {
existing, err := c.getHumanWriteModelByID(ctx, human.AggregateID, human.ResourceOwner)
if err != nil {
return nil, nil, err
}
if existing.UserState != domain.UserStateUnspecified {
return nil, nil, errors.ThrowPreconditionFailed(nil, "COMMAND-ziuna", "Errors.User.AlreadyExisting")
}
}
events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
if err != nil {
return nil, nil, err
@@ -355,8 +390,8 @@ func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Hum
if orgID == "" || !human.IsValid() {
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
}
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = true
if human.Password != nil && human.Password.SecretString != "" {
human.Password.ChangeRequired = true
}
return c.createHuman(ctx, orgID, human, nil, false, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
}
@@ -384,11 +419,11 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
if human != nil && human.Username == "" {
human.Username = human.EmailAddress
}
if orgID == "" || !human.IsValid() || link == nil && (human.Password == nil || human.SecretString == "") {
if orgID == "" || !human.IsValid() || link == nil && (human.Password == nil || human.Password.SecretString == "") {
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-9dk45", "Errors.User.Invalid")
}
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = false
if human.Password != nil && human.Password.SecretString != "" {
human.Password.ChangeRequired = false
}
return c.createHuman(ctx, orgID, human, link, true, false, domainPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
}
@@ -410,14 +445,18 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
return nil, nil, errors.ThrowInvalidArgument(nil, "COMMAND-SFd21", "Errors.User.DomainNotAllowedAsUsername")
}
}
userID, err := c.idGenerator.Next()
if err != nil {
return nil, nil, err
if human.AggregateID == "" {
userID, err := c.idGenerator.Next()
if err != nil {
return nil, nil, err
}
human.AggregateID = userID
}
human.AggregateID = userID
human.SetNamesAsDisplayname()
if human.Password != nil {
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, human.ChangeRequired); err != nil {
if err := human.HashPasswordIfExisting(pwPolicy, c.userPasswordAlg, human.Password.ChangeRequired); err != nil {
return nil, nil, err
}
}
@@ -510,7 +549,10 @@ func createAddHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, h
human.StreetAddress)
}
if human.Password != nil {
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
addEvent.AddPasswordData(human.Password.SecretCrypto, human.Password.ChangeRequired)
}
if human.HashedPassword != nil {
addEvent.AddPasswordData(human.HashedPassword.SecretCrypto, false)
}
return addEvent
}
@@ -541,7 +583,10 @@ func createRegisterHumanEvent(ctx context.Context, aggregate *eventstore.Aggrega
human.StreetAddress)
}
if human.Password != nil {
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
addEvent.AddPasswordData(human.Password.SecretCrypto, human.Password.ChangeRequired)
}
if human.HashedPassword != nil {
addEvent.AddPasswordData(human.HashedPassword.SecretCrypto, false)
}
return addEvent
}

View File

@@ -2,6 +2,7 @@ package command
import (
"context"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/domain"
@@ -11,6 +12,28 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) ImportHumanOTP(ctx context.Context, userID, userAgentID, resourceowner string, key string) error {
encryptedSecret, err := crypto.Encrypt([]byte(key), c.multifactors.OTP.CryptoMFA)
if err != nil {
return err
}
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
if err != nil {
return err
}
if otpWriteModel.State == domain.MFAStateReady {
return caos_errs.ThrowAlreadyExists(nil, "COMMAND-do9se", "Errors.User.MFA.OTP.AlreadyReady")
}
userAgg := UserAggregateFromWriteModel(&otpWriteModel.WriteModel)
_, err = c.eventstore.Push(ctx,
user.NewHumanOTPAddedEvent(ctx, userAgg, encryptedSecret),
user.NewHumanOTPVerifiedEvent(ctx, userAgg, userAgentID),
)
return err
}
func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string) (*domain.OTP, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
@@ -30,6 +53,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.DomainPolicy.NotFound")
}
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
if err != nil {
return nil, err
@@ -38,6 +62,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-do9se", "Errors.User.MFA.OTP.AlreadyReady")
}
userAgg := UserAggregateFromWriteModel(&otpWriteModel.WriteModel)
accountName := domain.GenerateLoginName(human.GetUsername(), org.PrimaryDomain, orgPolicy.UserLoginMustBeDomain)
if accountName == "" {
accountName = human.EmailAddress
@@ -46,8 +71,8 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
if err != nil {
return nil, err
}
_, err = c.eventstore.Push(ctx, user.NewHumanOTPAddedEvent(ctx, userAgg, secret))
_, err = c.eventstore.Push(ctx, user.NewHumanOTPAddedEvent(ctx, userAgg, secret))
if err != nil {
return nil, err
}

View File

@@ -11,6 +11,23 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddUserIDPLink(ctx context.Context, userID, resourceOwner string, link *domain.UserIDPLink) (err error) {
if userID == "" {
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing")
}
linkWriteModel := NewUserIDPLinkWriteModel(userID, link.IDPConfigID, link.ExternalUserID, resourceOwner)
userAgg := UserAggregateFromWriteModel(&linkWriteModel.WriteModel)
event, err := c.addUserIDPLink(ctx, userAgg, link)
if err != nil {
return err
}
_, err = c.eventstore.Push(ctx, event)
return err
}
func (c *Commands) BulkAddedUserIDPLinks(ctx context.Context, userID, resourceOwner string, links []*domain.UserIDPLink) (err error) {
if userID == "" {
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-03j8f", "Errors.IDMissing")

View File

@@ -2,7 +2,6 @@ package command
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/repository/user"
@@ -24,6 +23,29 @@ func (c *Commands) AddMachine(ctx context.Context, orgID string, machine *domain
if err != nil {
return nil, err
}
return c.addMachineWithID(ctx, orgID, userID, machine, domainPolicy)
}
func (c *Commands) AddMachineWithID(ctx context.Context, orgID string, userID string, machine *domain.Machine) (*domain.Machine, error) {
existingMachine, err := c.machineWriteModelByID(ctx, userID, orgID)
if err != nil {
return nil, err
}
if isUserStateExists(existingMachine.UserState) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-k2una", "Errors.User.AlreadyExisting")
}
domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.DomainPolicy.NotFound")
}
if !domainPolicy.UserLoginMustBeDomain {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0dd", "Errors.User.Invalid")
}
return c.addMachineWithID(ctx, orgID, userID, machine, domainPolicy)
}
func (c *Commands) addMachineWithID(ctx context.Context, orgID string, userID string, machine *domain.Machine, domainPolicy *domain.DomainPolicy) (*domain.Machine, error) {
machine.AggregateID = userID
addedMachine := NewMachineWriteModel(machine.AggregateID, orgID)
userAgg := UserAggregateFromWriteModel(&addedMachine.WriteModel)