mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-03 09:20:54 +00:00
fix: claim verified domain from usernames (#603)
* fix: return orgDomain validationType * added missing translations for orgDomain activity * claim org domain * show message if domain token was requested * fix tests * fix tests Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
parent
406924bed8
commit
1a00faf132
@ -1,5 +1,6 @@
|
|||||||
SystemDefaults:
|
SystemDefaults:
|
||||||
DefaultLanguage: 'de'
|
DefaultLanguage: 'de'
|
||||||
|
DefaultDomain: $ZITADEL_DEFAULT_DOMAIN
|
||||||
ZitadelDocs:
|
ZitadelDocs:
|
||||||
Issuer: $ZITADEL_ISSUER
|
Issuer: $ZITADEL_ISSUER
|
||||||
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
|
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
<p class="desc warn">{{ 'ORG.PAGES.ORGDOMAIN_VERIFICATION_VALIDATION_DESC' | translate }}</p>
|
<p class="desc warn">{{ 'ORG.PAGES.ORGDOMAIN_VERIFICATION_VALIDATION_DESC' | translate }}</p>
|
||||||
|
|
||||||
|
|
||||||
<div class="btn-container">
|
<p *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED" class="desc">
|
||||||
|
{{'ORG.PAGES.ORGDOMAIN_VERIFICATION_VALIDATION_ONGOING' | translate: domain }}</p>
|
||||||
|
<div *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED"
|
||||||
|
class="btn-container">
|
||||||
<button color="primary" type="submit" mat-raised-button
|
<button color="primary" type="submit" mat-raised-button
|
||||||
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
|
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,18 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef.afterClosed().subscribe(resp => {
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
if (resp) {
|
if (resp) {
|
||||||
this.orgService.AddMyOrgDomain(resp).then(domain => {
|
this.orgService.AddMyOrgDomain(resp).then(domain => {
|
||||||
this.domains.push(domain.toObject());
|
const newDomain = domain;
|
||||||
|
|
||||||
|
const newDomainView = new OrgDomainView();
|
||||||
|
newDomainView.setChangeDate(newDomain.getChangeDate());
|
||||||
|
newDomainView.setCreationDate(newDomain.getCreationDate());
|
||||||
|
newDomainView.setDomain(newDomain.getDomain());
|
||||||
|
newDomainView.setOrgId(newDomain.getOrgId());
|
||||||
|
newDomainView.setPrimary(newDomain.getPrimary());
|
||||||
|
newDomainView.setSequence(newDomain.getSequence());
|
||||||
|
newDomainView.setVerified(newDomain.getVerified());
|
||||||
|
|
||||||
|
this.domains.push(newDomainView.toObject());
|
||||||
this.toast.showInfo('ORG.TOAST.DOMAINADDED', true);
|
this.toast.showInfo('ORG.TOAST.DOMAINADDED', true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,7 @@
|
|||||||
"ORGDOMAIN_VERIFICATION_VALIDATION_DESC":"Die Tokens werden regelmäßig überprüft, um sicherzustellen, dass sie weiterhin Besitzer der Domain sind.",
|
"ORGDOMAIN_VERIFICATION_VALIDATION_DESC":"Die Tokens werden regelmäßig überprüft, um sicherzustellen, dass sie weiterhin Besitzer der Domain sind.",
|
||||||
"ORGDOMAIN_VERIFICATION_NEWTOKEN_TITLE":"Neues Token anfordern",
|
"ORGDOMAIN_VERIFICATION_NEWTOKEN_TITLE":"Neues Token anfordern",
|
||||||
"ORGDOMAIN_VERIFICATION_NEWTOKEN_DESC":"Wenn Sie ein neues Token anfordern wollen, klicken Sie auf die gewünschte Methode. Wenn Sie ein vorhandenes Token validieren möchten Klicken Sie auf Validieren.",
|
"ORGDOMAIN_VERIFICATION_NEWTOKEN_DESC":"Wenn Sie ein neues Token anfordern wollen, klicken Sie auf die gewünschte Methode. Wenn Sie ein vorhandenes Token validieren möchten Klicken Sie auf Validieren.",
|
||||||
|
"ORGDOMAIN_VERIFICATION_VALIDATION_ONGOING":"Ein Verifikationstoken wurde bereits angefragt. Klicke auf dem Button um dieses zu verifizieren!",
|
||||||
"DOWNLOAD_FILE":"Datei download",
|
"DOWNLOAD_FILE":"Datei download",
|
||||||
"SELECTORGTOOLTIP":"Wähle diese Organisation",
|
"SELECTORGTOOLTIP":"Wähle diese Organisation",
|
||||||
"PRIMARYDOMAIN":"Primäre Domain",
|
"PRIMARYDOMAIN":"Primäre Domain",
|
||||||
|
@ -285,6 +285,7 @@
|
|||||||
"ORGDOMAIN_VERIFICATION_VALIDATION_DESC":"The tokens are checked regularly to ensure you are still owner of the domain.",
|
"ORGDOMAIN_VERIFICATION_VALIDATION_DESC":"The tokens are checked regularly to ensure you are still owner of the domain.",
|
||||||
"ORGDOMAIN_VERIFICATION_NEWTOKEN_TITLE":"Request new token",
|
"ORGDOMAIN_VERIFICATION_NEWTOKEN_TITLE":"Request new token",
|
||||||
"ORGDOMAIN_VERIFICATION_NEWTOKEN_DESC":"If you want to request a new token, select you preferred method, if you want to validate a persisting token, click on the button above.",
|
"ORGDOMAIN_VERIFICATION_NEWTOKEN_DESC":"If you want to request a new token, select you preferred method, if you want to validate a persisting token, click on the button above.",
|
||||||
|
"ORGDOMAIN_VERIFICATION_VALIDATION_ONGOING":"A verification token has already been requested. Click on the button to trigger a verification check!",
|
||||||
"DOWNLOAD_FILE":"Download file",
|
"DOWNLOAD_FILE":"Download file",
|
||||||
"SELECTORGTOOLTIP":"Select this organisation",
|
"SELECTORGTOOLTIP":"Select this organisation",
|
||||||
"PRIMARYDOMAIN":"Primary Domain",
|
"PRIMARYDOMAIN":"Primary Domain",
|
||||||
|
@ -2,11 +2,13 @@ package eventstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
admin_model "github.com/caos/zitadel/internal/admin/model"
|
admin_model "github.com/caos/zitadel/internal/admin/model"
|
||||||
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||||
org_model "github.com/caos/zitadel/internal/org/model"
|
org_model "github.com/caos/zitadel/internal/org/model"
|
||||||
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||||
@ -40,7 +42,14 @@ func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, setUp.Org)
|
users := func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) {
|
||||||
|
userIDs, err := repo.View.UserIDsByDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEventstore.PrepareDomainClaimed(ctx, userIDs)
|
||||||
|
}
|
||||||
|
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, setUp.Org, users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ import (
|
|||||||
|
|
||||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
||||||
"github.com/caos/zitadel/internal/config/types"
|
"github.com/caos/zitadel/internal/config/types"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
"github.com/caos/zitadel/internal/eventstore/query"
|
"github.com/caos/zitadel/internal/eventstore/query"
|
||||||
|
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||||
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,12 +26,14 @@ type handler struct {
|
|||||||
|
|
||||||
type EventstoreRepos struct {
|
type EventstoreRepos struct {
|
||||||
UserEvents *usr_event.UserEventstore
|
UserEvents *usr_event.UserEventstore
|
||||||
|
OrgEvents *org_event.OrgEventstore
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, repos EventstoreRepos) []query.Handler {
|
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos) []query.Handler {
|
||||||
return []query.Handler{
|
return []query.Handler{
|
||||||
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
|
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
|
||||||
&IamMember{handler: handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount}, userEvents: repos.UserEvents},
|
&IamMember{handler: handler{view, bulkLimit, configs.cycleDuration("IamMember"), errorCount}, userEvents: repos.UserEvents},
|
||||||
|
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, eventstore: eventstore, orgEvents: repos.OrgEvents},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
177
internal/admin/repository/eventsourcing/handler/user.go
Normal file
177
internal/admin/repository/eventsourcing/handler/user.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/logging"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
|
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||||
|
org_model "github.com/caos/zitadel/internal/org/model"
|
||||||
|
org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||||
|
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||||
|
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||||
|
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
handler
|
||||||
|
eventstore eventstore.Eventstore
|
||||||
|
orgEvents *org_events.OrgEventstore
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
userTable = "adminapi.users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *User) ViewModel() string {
|
||||||
|
return userTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) EventQuery() (*models.SearchQuery, error) {
|
||||||
|
sequence, err := u.view.GetLatestUserSequence()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return es_models.NewSearchQuery().
|
||||||
|
AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate).
|
||||||
|
LatestSequenceFilter(sequence.CurrentSequence), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) Reduce(event *models.Event) (err error) {
|
||||||
|
switch event.AggregateType {
|
||||||
|
case es_model.UserAggregate:
|
||||||
|
return u.ProcessUser(event)
|
||||||
|
case org_es_model.OrgAggregate:
|
||||||
|
return u.ProcessOrg(event)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) ProcessUser(event *models.Event) (err error) {
|
||||||
|
user := new(view_model.UserView)
|
||||||
|
switch event.Type {
|
||||||
|
case es_model.UserAdded,
|
||||||
|
es_model.UserRegistered:
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.fillLoginNames(user)
|
||||||
|
case es_model.UserProfileChanged,
|
||||||
|
es_model.UserEmailChanged,
|
||||||
|
es_model.UserEmailVerified,
|
||||||
|
es_model.UserPhoneChanged,
|
||||||
|
es_model.UserPhoneVerified,
|
||||||
|
es_model.UserPhoneRemoved,
|
||||||
|
es_model.UserAddressChanged,
|
||||||
|
es_model.UserDeactivated,
|
||||||
|
es_model.UserReactivated,
|
||||||
|
es_model.UserLocked,
|
||||||
|
es_model.UserUnlocked,
|
||||||
|
es_model.MfaOtpAdded,
|
||||||
|
es_model.MfaOtpVerified,
|
||||||
|
es_model.MfaOtpRemoved:
|
||||||
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
case es_model.DomainClaimed:
|
||||||
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.fillLoginNames(user)
|
||||||
|
case es_model.UserRemoved:
|
||||||
|
err = u.view.DeleteUser(event.AggregateID, event.Sequence)
|
||||||
|
default:
|
||||||
|
return u.view.ProcessedUserSequence(event.Sequence)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return u.view.PutUser(user, user.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) ProcessOrg(event *models.Event) (err error) {
|
||||||
|
switch event.Type {
|
||||||
|
case org_es_model.OrgDomainVerified,
|
||||||
|
org_es_model.OrgDomainRemoved,
|
||||||
|
org_es_model.OrgIamPolicyAdded,
|
||||||
|
org_es_model.OrgIamPolicyChanged,
|
||||||
|
org_es_model.OrgIamPolicyRemoved:
|
||||||
|
return u.fillLoginNamesOnOrgUsers(event)
|
||||||
|
case org_es_model.OrgDomainPrimarySet:
|
||||||
|
return u.fillPreferredLoginNamesOnOrgUsers(event)
|
||||||
|
default:
|
||||||
|
return u.view.ProcessedUserSequence(event.Sequence)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) fillLoginNamesOnOrgUsers(event *models.Event) error {
|
||||||
|
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
users, err := u.view.UsersByOrgID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, user := range users {
|
||||||
|
user.SetLoginNames(policy, org.Domains)
|
||||||
|
}
|
||||||
|
return u.view.PutUsers(users, event.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error {
|
||||||
|
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !policy.UserLoginMustBeDomain {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
users, err := u.view.UsersByOrgID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, user := range users {
|
||||||
|
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
|
||||||
|
}
|
||||||
|
return u.view.PutUsers(users, event.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
|
||||||
|
org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(user.ResourceOwner))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), user.ResourceOwner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user.SetLoginNames(policy, org.Domains)
|
||||||
|
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) OnError(event *models.Event, err error) error {
|
||||||
|
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
||||||
|
return spooler.HandleError(event, err, u.view.GetLatestUserFailedEvent, u.view.ProcessedUserFailedEvent, u.view.ProcessedUserSequence, u.errorCountUntilSkip)
|
||||||
|
}
|
@ -86,7 +86,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, r
|
|||||||
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
|
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
|
||||||
logging.Log("SERVE-djs3R").OnError(err).Panic("failed to execute setup")
|
logging.Log("SERVE-djs3R").OnError(err).Panic("failed to execute setup")
|
||||||
|
|
||||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, handler.EventstoreRepos{UserEvents: user})
|
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, handler.EventstoreRepos{UserEvents: user, OrgEvents: org})
|
||||||
|
|
||||||
return &EsRepository{
|
return &EsRepository{
|
||||||
spooler: spool,
|
spooler: spool,
|
||||||
|
@ -211,7 +211,7 @@ func (setUp *initializer) org(ctx context.Context, org types.Org) (*org_model.Or
|
|||||||
Name: org.Name,
|
Name: org.Name,
|
||||||
Domains: []*org_model.OrgDomain{{Domain: org.Domain}},
|
Domains: []*org_model.OrgDomain{{Domain: org.Domain}},
|
||||||
}
|
}
|
||||||
return setUp.repos.OrgEvents.CreateOrg(ctx, createOrg)
|
return setUp.repos.OrgEvents.CreateOrg(ctx, createOrg, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (setUp *initializer) iamorgpolicy(ctx context.Context, org *org_model.Org) (*org_model.OrgIamPolicy, error) {
|
func (setUp *initializer) iamorgpolicy(ctx context.Context, org *org_model.Org) (*org_model.OrgIamPolicy, error) {
|
||||||
|
@ -21,7 +21,7 @@ func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sq
|
|||||||
Eventstore: es,
|
Eventstore: es,
|
||||||
Locker: &locker{dbClient: sql},
|
Locker: &locker{dbClient: sql},
|
||||||
ConcurrentWorkers: c.ConcurrentWorkers,
|
ConcurrentWorkers: c.ConcurrentWorkers,
|
||||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, repos),
|
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, repos),
|
||||||
}
|
}
|
||||||
spool := spoolerConfig.New()
|
spool := spoolerConfig.New()
|
||||||
spool.Start()
|
spool.Start()
|
||||||
|
83
internal/admin/repository/eventsourcing/view/user.go
Normal file
83
internal/admin/repository/eventsourcing/view/user.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view"
|
||||||
|
"github.com/caos/zitadel/internal/user/repository/view/model"
|
||||||
|
"github.com/caos/zitadel/internal/view/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
userTable = "adminapi.users"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *View) UserByID(userID string) (*model.UserView, error) {
|
||||||
|
return view.UserByID(v.Db, userTable, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
|
||||||
|
return view.SearchUsers(v.Db, userTable, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetGlobalUserByEmail(email string) (*model.UserView, error) {
|
||||||
|
return view.GetGlobalUserByEmail(v.Db, userTable, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
|
||||||
|
return view.UsersByOrgID(v.Db, userTable, orgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserIDsByDomain(domain string) ([]string, error) {
|
||||||
|
return view.UserIDsByDomain(v.Db, userTable, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) IsUserUnique(userName, email string) (bool, error) {
|
||||||
|
return view.IsUserUnique(v.Db, userTable, userName, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) UserMfas(userID string) ([]*usr_model.MultiFactor, error) {
|
||||||
|
return view.UserMfas(v.Db, userTable, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) PutUsers(user []*model.UserView, sequence uint64) error {
|
||||||
|
err := view.PutUsers(v.Db, userTable, user...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSequence(sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) PutUser(user *model.UserView, sequence uint64) error {
|
||||||
|
err := view.PutUser(v.Db, userTable, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sequence != 0 {
|
||||||
|
return v.ProcessedUserSequence(sequence)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) DeleteUser(userID string, eventSequence uint64) error {
|
||||||
|
err := view.DeleteUser(v.Db, userTable, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v.ProcessedUserSequence(eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserSequence() (*repository.CurrentSequence, error) {
|
||||||
|
return v.latestSequence(userTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserSequence(eventSequence uint64) error {
|
||||||
|
return v.saveCurrentSequence(userTable, eventSequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) GetLatestUserFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
|
||||||
|
return v.latestFailedEvent(userTable, sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *View) ProcessedUserFailedEvent(failedEvent *repository.FailedEvent) error {
|
||||||
|
return v.saveFailedEvent(failedEvent)
|
||||||
|
}
|
@ -90,6 +90,17 @@ func orgDomainValidationTypeToModel(key management.OrgDomainValidationType) org_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func orgDomainValidationTypeFromModel(key org_model.OrgDomainValidationType) management.OrgDomainValidationType {
|
||||||
|
switch key {
|
||||||
|
case org_model.OrgDomainValidationTypeHTTP:
|
||||||
|
return management.OrgDomainValidationType_ORGDOMAINVALIDATIONTYPE_HTTP
|
||||||
|
case org_model.OrgDomainValidationTypeDNS:
|
||||||
|
return management.OrgDomainValidationType_ORGDOMAINVALIDATIONTYPE_DNS
|
||||||
|
default:
|
||||||
|
return management.OrgDomainValidationType_ORGDOMAINVALIDATIONTYPE_UNSPECIFIED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func primaryOrgDomainToModel(domain *management.PrimaryOrgDomainRequest) *org_model.OrgDomain {
|
func primaryOrgDomainToModel(domain *management.PrimaryOrgDomainRequest) *org_model.OrgDomain {
|
||||||
return &org_model.OrgDomain{Domain: domain.Domain}
|
return &org_model.OrgDomain{Domain: domain.Domain}
|
||||||
}
|
}
|
||||||
@ -125,6 +136,7 @@ func orgDomainViewFromModel(domain *org_model.OrgDomainView) *management.OrgDoma
|
|||||||
Domain: domain.Domain,
|
Domain: domain.Domain,
|
||||||
Verified: domain.Verified,
|
Verified: domain.Verified,
|
||||||
Primary: domain.Primary,
|
Primary: domain.Primary,
|
||||||
|
ValidationType: orgDomainValidationTypeFromModel(domain.ValidationType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,12 @@ package eventstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
auth_model "github.com/caos/zitadel/internal/auth/model"
|
auth_model "github.com/caos/zitadel/internal/auth/model"
|
||||||
auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||||
org_model "github.com/caos/zitadel/internal/org/model"
|
org_model "github.com/caos/zitadel/internal/org/model"
|
||||||
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||||
@ -57,7 +60,14 @@ func (repo *OrgRepository) RegisterOrg(ctx context.Context, register *auth_model
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, register.Org)
|
users := func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) {
|
||||||
|
userIDs, err := repo.View.UserIDsByDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEventstore.PrepareDomainClaimed(ctx, userIDs)
|
||||||
|
}
|
||||||
|
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, register.Org, users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,12 @@ const (
|
|||||||
userTable = "auth.users"
|
userTable = "auth.users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *User) ViewModel() string {
|
func (u *User) ViewModel() string {
|
||||||
return userTable
|
return userTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) EventQuery() (*models.SearchQuery, error) {
|
func (u *User) EventQuery() (*models.SearchQuery, error) {
|
||||||
sequence, err := p.view.GetLatestUserSequence()
|
sequence, err := u.view.GetLatestUserSequence()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ func (u *User) Reduce(event *models.Event) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) ProcessUser(event *models.Event) (err error) {
|
func (u *User) ProcessUser(event *models.Event) (err error) {
|
||||||
user := new(view_model.UserView)
|
user := new(view_model.UserView)
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case es_model.UserAdded,
|
case es_model.UserAdded,
|
||||||
@ -61,7 +61,7 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.fillLoginNames(user)
|
u.fillLoginNames(user)
|
||||||
case es_model.UserProfileChanged,
|
case es_model.UserProfileChanged,
|
||||||
es_model.UserEmailChanged,
|
es_model.UserEmailChanged,
|
||||||
es_model.UserEmailVerified,
|
es_model.UserEmailVerified,
|
||||||
@ -78,20 +78,30 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
|
|||||||
es_model.MfaOtpRemoved,
|
es_model.MfaOtpRemoved,
|
||||||
es_model.MfaInitSkipped,
|
es_model.MfaInitSkipped,
|
||||||
es_model.UserPasswordChanged:
|
es_model.UserPasswordChanged:
|
||||||
user, err = p.view.UserByID(event.AggregateID)
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = user.AppendEvent(event)
|
err = user.AppendEvent(event)
|
||||||
|
case es_model.DomainClaimed:
|
||||||
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.fillLoginNames(user)
|
||||||
case es_model.UserRemoved:
|
case es_model.UserRemoved:
|
||||||
err = p.view.DeleteUser(event.AggregateID, event.Sequence)
|
err = u.view.DeleteUser(event.AggregateID, event.Sequence)
|
||||||
default:
|
default:
|
||||||
return p.view.ProcessedUserSequence(event.Sequence)
|
return u.view.ProcessedUserSequence(event.Sequence)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.view.PutUser(user, user.Sequence)
|
return u.view.PutUser(user, user.Sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
|
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
|
||||||
@ -172,7 +182,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) OnError(event *models.Event, err error) error {
|
func (u *User) OnError(event *models.Event, err error) error {
|
||||||
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
||||||
return spooler.HandleError(event, err, p.view.GetLatestUserFailedEvent, p.view.ProcessedUserFailedEvent, p.view.ProcessedUserSequence, p.errorCountUntilSkip)
|
return spooler.HandleError(event, err, u.view.GetLatestUserFailedEvent, u.view.ProcessedUserFailedEvent, u.view.ProcessedUserSequence, u.errorCountUntilSkip)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ func (v *View) UserByLoginName(loginName string) (*model.UserView, error) {
|
|||||||
func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
|
func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
|
||||||
return view.UsersByOrgID(v.Db, userTable, orgID)
|
return view.UsersByOrgID(v.Db, userTable, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *View) UserIDsByDomain(domain string) ([]string, error) {
|
||||||
|
return view.UserIDsByDomain(v.Db, userTable, domain)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
|
func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
|
||||||
return view.SearchUsers(v.Db, userTable, request)
|
return view.SearchUsers(v.Db, userTable, request)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
type SystemDefaults struct {
|
type SystemDefaults struct {
|
||||||
DefaultLanguage language.Tag
|
DefaultLanguage language.Tag
|
||||||
|
DefaultDomain string
|
||||||
ZitadelDocs ZitadelDocs
|
ZitadelDocs ZitadelDocs
|
||||||
SecretGenerators SecretGenerators
|
SecretGenerators SecretGenerators
|
||||||
UserVerificationKey *crypto.KeyConfig
|
UserVerificationKey *crypto.KeyConfig
|
||||||
|
@ -2,11 +2,12 @@ package eventstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||||
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||||
global_model "github.com/caos/zitadel/internal/model"
|
global_model "github.com/caos/zitadel/internal/model"
|
||||||
@ -15,8 +16,6 @@ import (
|
|||||||
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||||
"github.com/caos/zitadel/internal/org/repository/view/model"
|
"github.com/caos/zitadel/internal/org/repository/view/model"
|
||||||
usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,7 +47,7 @@ func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (repo *OrgRepository) CreateOrg(ctx context.Context, name string) (*org_model.Org, error) {
|
func (repo *OrgRepository) CreateOrg(ctx context.Context, name string) (*org_model.Org, error) {
|
||||||
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, &org_model.Org{Name: name})
|
org, aggregates, err := repo.OrgEventstore.PrepareCreateOrg(ctx, &org_model.Org{Name: name}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -118,7 +117,14 @@ func (repo *OrgRepository) GenerateMyOrgDomainValidation(ctx context.Context, do
|
|||||||
|
|
||||||
func (repo *OrgRepository) ValidateMyOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
|
func (repo *OrgRepository) ValidateMyOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
|
||||||
domain.AggregateID = authz.GetCtxData(ctx).OrgID
|
domain.AggregateID = authz.GetCtxData(ctx).OrgID
|
||||||
return repo.OrgEventstore.ValidateOrgDomain(ctx, domain)
|
users := func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) {
|
||||||
|
userIDs, err := repo.View.UserIDsByDomain(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return repo.UserEvents.PrepareDomainClaimed(ctx, userIDs)
|
||||||
|
}
|
||||||
|
return repo.OrgEventstore.ValidateOrgDomain(ctx, domain, users)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *OrgRepository) SetMyPrimaryOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
|
func (repo *OrgRepository) SetMyPrimaryOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
|
||||||
|
@ -45,7 +45,8 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) {
|
|||||||
switch event.Type {
|
switch event.Type {
|
||||||
case model.OrgDomainAdded:
|
case model.OrgDomainAdded:
|
||||||
err = domain.AppendEvent(event)
|
err = domain.AppendEvent(event)
|
||||||
case model.OrgDomainVerified:
|
case model.OrgDomainVerified,
|
||||||
|
model.OrgDomainVerificationAdded:
|
||||||
err = domain.SetData(event)
|
err = domain.SetData(event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -26,12 +26,12 @@ const (
|
|||||||
userTable = "management.users"
|
userTable = "management.users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *User) ViewModel() string {
|
func (u *User) ViewModel() string {
|
||||||
return userTable
|
return userTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) EventQuery() (*models.SearchQuery, error) {
|
func (u *User) EventQuery() (*models.SearchQuery, error) {
|
||||||
sequence, err := p.view.GetLatestUserSequence()
|
sequence, err := u.view.GetLatestUserSequence()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ func (u *User) Reduce(event *models.Event) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) ProcessUser(event *models.Event) (err error) {
|
func (u *User) ProcessUser(event *models.Event) (err error) {
|
||||||
user := new(view_model.UserView)
|
user := new(view_model.UserView)
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case es_model.UserAdded,
|
case es_model.UserAdded,
|
||||||
@ -60,7 +60,7 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = p.fillLoginNames(user)
|
err = u.fillLoginNames(user)
|
||||||
case es_model.UserProfileChanged,
|
case es_model.UserProfileChanged,
|
||||||
es_model.UserEmailChanged,
|
es_model.UserEmailChanged,
|
||||||
es_model.UserEmailVerified,
|
es_model.UserEmailVerified,
|
||||||
@ -75,20 +75,30 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
|
|||||||
es_model.MfaOtpAdded,
|
es_model.MfaOtpAdded,
|
||||||
es_model.MfaOtpVerified,
|
es_model.MfaOtpVerified,
|
||||||
es_model.MfaOtpRemoved:
|
es_model.MfaOtpRemoved:
|
||||||
user, err = p.view.UserByID(event.AggregateID)
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = user.AppendEvent(event)
|
err = user.AppendEvent(event)
|
||||||
|
case es_model.DomainClaimed:
|
||||||
|
user, err = u.view.UserByID(event.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = user.AppendEvent(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = u.fillLoginNames(user)
|
||||||
case es_model.UserRemoved:
|
case es_model.UserRemoved:
|
||||||
err = p.view.DeleteUser(event.AggregateID, event.Sequence)
|
err = u.view.DeleteUser(event.AggregateID, event.Sequence)
|
||||||
default:
|
default:
|
||||||
return p.view.ProcessedUserSequence(event.Sequence)
|
return u.view.ProcessedUserSequence(event.Sequence)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.view.PutUser(user, user.Sequence)
|
return u.view.PutUser(user, user.Sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) ProcessOrg(event *models.Event) (err error) {
|
func (u *User) ProcessOrg(event *models.Event) (err error) {
|
||||||
@ -161,7 +171,7 @@ func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *User) OnError(event *models.Event, err error) error {
|
func (u *User) OnError(event *models.Event, err error) error {
|
||||||
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
logging.LogWithFields("SPOOL-is8wa", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
||||||
return spooler.HandleError(event, err, p.view.GetLatestUserFailedEvent, p.view.ProcessedUserFailedEvent, p.view.ProcessedUserSequence, p.errorCountUntilSkip)
|
return spooler.HandleError(event, err, u.view.GetLatestUserFailedEvent, u.view.ProcessedUserFailedEvent, u.view.ProcessedUserSequence, u.errorCountUntilSkip)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,10 @@ func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
|
|||||||
return view.UsersByOrgID(v.Db, userTable, orgID)
|
return view.UsersByOrgID(v.Db, userTable, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *View) UserIDsByDomain(domain string) ([]string, error) {
|
||||||
|
return view.UserIDsByDomain(v.Db, userTable, domain)
|
||||||
|
}
|
||||||
|
|
||||||
func (v *View) IsUserUnique(userName, email string) (bool, error) {
|
func (v *View) IsUserUnique(userName, email string) (bool, error) {
|
||||||
return view.IsUserUnique(v.Db, userTable, userName, email)
|
return view.IsUserUnique(v.Db, userTable, userName, email)
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,6 @@ const (
|
|||||||
SearchMethodLessThan
|
SearchMethodLessThan
|
||||||
SearchMethodIsOneOf
|
SearchMethodIsOneOf
|
||||||
SearchMethodListContains
|
SearchMethodListContains
|
||||||
|
SearchMethodEndsWith
|
||||||
|
SearchMethodEndsWithIgnoreCase
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/model"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OrgDomainView struct {
|
type OrgDomainView struct {
|
||||||
@ -12,6 +13,7 @@ type OrgDomainView struct {
|
|||||||
Domain string
|
Domain string
|
||||||
Primary bool
|
Primary bool
|
||||||
Verified bool
|
Verified bool
|
||||||
|
ValidationType OrgDomainValidationType
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrgDomainSearchRequest struct {
|
type OrgDomainSearchRequest struct {
|
||||||
|
@ -53,7 +53,7 @@ func StartOrg(conf OrgConfig, defaults systemdefaults.SystemDefaults) *OrgEvents
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *OrgEventstore) PrepareCreateOrg(ctx context.Context, orgModel *org_model.Org) (*model.Org, []*es_models.Aggregate, error) {
|
func (es *OrgEventstore) PrepareCreateOrg(ctx context.Context, orgModel *org_model.Org, users func(context.Context, string) ([]*es_models.Aggregate, error)) (*model.Org, []*es_models.Aggregate, error) {
|
||||||
if orgModel == nil || !orgModel.IsValid() {
|
if orgModel == nil || !orgModel.IsValid() {
|
||||||
return nil, nil, errors.ThrowInvalidArgument(nil, "EVENT-OeLSk", "Errors.Org.Invalid")
|
return nil, nil, errors.ThrowInvalidArgument(nil, "EVENT-OeLSk", "Errors.Org.Invalid")
|
||||||
}
|
}
|
||||||
@ -66,13 +66,13 @@ func (es *OrgEventstore) PrepareCreateOrg(ctx context.Context, orgModel *org_mod
|
|||||||
orgModel.AggregateID = id
|
orgModel.AggregateID = id
|
||||||
org := model.OrgFromModel(orgModel)
|
org := model.OrgFromModel(orgModel)
|
||||||
|
|
||||||
aggregates, err := orgCreatedAggregates(ctx, es.AggregateCreator(), org)
|
aggregates, err := orgCreatedAggregates(ctx, es.AggregateCreator(), org, users)
|
||||||
|
|
||||||
return org, aggregates, err
|
return org, aggregates, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *OrgEventstore) CreateOrg(ctx context.Context, orgModel *org_model.Org) (*org_model.Org, error) {
|
func (es *OrgEventstore) CreateOrg(ctx context.Context, orgModel *org_model.Org, users func(context.Context, string) ([]*es_models.Aggregate, error)) (*org_model.Org, error) {
|
||||||
org, aggregates, err := es.PrepareCreateOrg(ctx, orgModel)
|
org, aggregates, err := es.PrepareCreateOrg(ctx, orgModel, users)
|
||||||
err = es_sdk.PushAggregates(ctx, es.PushAggregates, org.AppendEvents, aggregates...)
|
err = es_sdk.PushAggregates(ctx, es.PushAggregates, org.AppendEvents, aggregates...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -222,7 +222,7 @@ func (es *OrgEventstore) GenerateOrgDomainValidation(ctx context.Context, domain
|
|||||||
return token, url, err
|
return token, url, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *OrgEventstore) ValidateOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
|
func (es *OrgEventstore) ValidateOrgDomain(ctx context.Context, domain *org_model.OrgDomain, users func(context.Context, string) ([]*es_models.Aggregate, error)) error {
|
||||||
if domain == nil || !domain.IsValid() {
|
if domain == nil || !domain.IsValid() {
|
||||||
return errors.ThrowPreconditionFailed(nil, "EVENT-R24hb", "Errors.Org.InvalidDomain")
|
return errors.ThrowPreconditionFailed(nil, "EVENT-R24hb", "Errors.Org.InvalidDomain")
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ func (es *OrgEventstore) ValidateOrgDomain(ctx context.Context, domain *org_mode
|
|||||||
checkType, _ := d.ValidationType.CheckType()
|
checkType, _ := d.ValidationType.CheckType()
|
||||||
err = es.verificationValidator(d.Domain, validationCode, validationCode, checkType)
|
err = es.verificationValidator(d.Domain, validationCode, validationCode, checkType)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
orgAggregates, err := OrgDomainVerifiedAggregate(ctx, es.Eventstore.AggregateCreator(), repoOrg, repoDomain)
|
orgAggregates, err := OrgDomainVerifiedAggregate(ctx, es.Eventstore.AggregateCreator(), repoOrg, repoDomain, users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -578,6 +578,7 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
|
|||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
domain *org_model.OrgDomain
|
domain *org_model.OrgDomain
|
||||||
|
users func(ctx context.Context, domain string) ([]*es_models.Aggregate, error)
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -682,6 +683,26 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
|
|||||||
isErr: errors.IsInternal,
|
isErr: errors.IsInternal,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "(user) aggregate fails",
|
||||||
|
fields: fields{Eventstore: newTestEventstore(t).
|
||||||
|
expectFilterEvents([]*es_models.Event{orgCreatedEvent(), orgDomainAddedEvent(), orgDomainVerificationAddedEvent("token")}, nil).
|
||||||
|
expectDecrypt().
|
||||||
|
expectVerification(true).
|
||||||
|
expectAggregateCreator().
|
||||||
|
expectPushEvents(0, errors.ThrowInternal(nil, "EVENT-S8WzW", "test")),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
ctx: authz.NewMockContext("org", "user"),
|
||||||
|
domain: &org_model.OrgDomain{ObjectRoot: es_models.ObjectRoot{AggregateID: "hodor-org"}, Domain: "hodor.org", ValidationType: org_model.OrgDomainValidationTypeHTTP},
|
||||||
|
users: func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) {
|
||||||
|
return nil, errors.ThrowInternal(nil, "id", "internal error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
isErr: errors.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "push failed",
|
name: "push failed",
|
||||||
fields: fields{Eventstore: newTestEventstore(t).
|
fields: fields{Eventstore: newTestEventstore(t).
|
||||||
@ -719,7 +740,7 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
err := tt.fields.Eventstore.ValidateOrgDomain(tt.args.ctx, tt.args.domain)
|
err := tt.fields.Eventstore.ValidateOrgDomain(tt.args.ctx, tt.args.domain, tt.args.users)
|
||||||
if tt.res.isErr == nil && err != nil {
|
if tt.res.isErr == nil && err != nil {
|
||||||
t.Errorf("no error expected got:%T %v", err, err)
|
t.Errorf("no error expected got:%T %v", err, err)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package eventsourcing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||||
org_model "github.com/caos/zitadel/internal/org/model"
|
org_model "github.com/caos/zitadel/internal/org/model"
|
||||||
@ -42,7 +43,7 @@ func OrgAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, i
|
|||||||
return aggCreator.NewAggregate(ctx, id, model.OrgAggregate, model.OrgVersion, sequence)
|
return aggCreator.NewAggregate(ctx, id, model.OrgAggregate, model.OrgVersion, sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCreator, org *model.Org) (_ []*es_models.Aggregate, err error) {
|
func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCreator, org *model.Org, users func(context.Context, string) ([]*es_models.Aggregate, error)) (_ []*es_models.Aggregate, err error) {
|
||||||
if org == nil {
|
if org == nil {
|
||||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie7", "Errors.Internal")
|
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie7", "Errors.Internal")
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCr
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
aggregates := make([]*es_models.Aggregate, 0)
|
aggregates := make([]*es_models.Aggregate, 0)
|
||||||
aggregates, err = addDomainAggregateAndEvents(ctx, aggCreator, agg, aggregates, org)
|
aggregates, err = addDomainAggregateAndEvents(ctx, aggCreator, agg, aggregates, org, users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -68,22 +69,18 @@ func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCr
|
|||||||
return append(aggregates, agg), nil
|
return append(aggregates, agg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addDomainAggregateAndEvents(ctx context.Context, aggCreator *es_models.AggregateCreator, orgAggregate *es_models.Aggregate, aggregates []*es_models.Aggregate, org *model.Org) ([]*es_models.Aggregate, error) {
|
func addDomainAggregateAndEvents(ctx context.Context, aggCreator *es_models.AggregateCreator, orgAggregate *es_models.Aggregate, aggregates []*es_models.Aggregate, org *model.Org, users func(context.Context, string) ([]*es_models.Aggregate, error)) ([]*es_models.Aggregate, error) {
|
||||||
for _, domain := range org.Domains {
|
for _, domain := range org.Domains {
|
||||||
orgAggregate, err := orgAggregate.AppendEvent(model.OrgDomainAdded, domain)
|
orgAggregate, err := orgAggregate.AppendEvent(model.OrgDomainAdded, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if domain.Verified {
|
if domain.Verified {
|
||||||
domainAggregate, err := reservedUniqueDomainAggregate(ctx, aggCreator, org.AggregateID, domain.Domain)
|
domainAggregates, err := OrgDomainVerifiedAggregate(ctx, aggCreator, org, domain, users)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
aggregates = append(aggregates, domainAggregate)
|
|
||||||
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainVerified, domain)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
aggregates = append(aggregates, domainAggregates...)
|
||||||
}
|
}
|
||||||
if domain.Primary {
|
if domain.Primary {
|
||||||
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainPrimarySet, domain)
|
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainPrimarySet, domain)
|
||||||
@ -182,7 +179,6 @@ func reservedUniqueDomainAggregate(ctx context.Context, aggCreator *es_models.Ag
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return aggregate.SetPrecondition(OrgDomainUniqueQuery(domain), isEventValidation(aggregate, model.OrgDomainReserved)), nil
|
return aggregate.SetPrecondition(OrgDomainUniqueQuery(domain), isEventValidation(aggregate, model.OrgDomainReserved)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +269,7 @@ func OrgDomainValidationFailedAggregate(aggCreator *es_models.AggregateCreator,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func OrgDomainVerifiedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain) ([]*es_models.Aggregate, error) {
|
func OrgDomainVerifiedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain, users func(context.Context, string) ([]*es_models.Aggregate, error)) ([]*es_models.Aggregate, error) {
|
||||||
if domain == nil {
|
if domain == nil {
|
||||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-DHs7s", "Errors.Internal")
|
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-DHs7s", "Errors.Internal")
|
||||||
}
|
}
|
||||||
@ -291,6 +287,13 @@ func OrgDomainVerifiedAggregate(ctx context.Context, aggCreator *es_models.Aggre
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
aggregates = append(aggregates, domainAgregate)
|
aggregates = append(aggregates, domainAgregate)
|
||||||
|
if users != nil {
|
||||||
|
userAggregates, err := users(ctx, domain.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ThrowPreconditionFailed(err, "EVENT-HBwsw", "Errors.Internal")
|
||||||
|
}
|
||||||
|
aggregates = append(aggregates, userAggregates...)
|
||||||
|
}
|
||||||
return append(aggregates, agg), nil
|
return append(aggregates, agg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +466,7 @@ func TestOrgCreatedAggregates(t *testing.T) {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
aggCreator *es_models.AggregateCreator
|
aggCreator *es_models.AggregateCreator
|
||||||
org *model.Org
|
org *model.Org
|
||||||
|
users func(ctx context.Context, domain string) ([]*es_models.Aggregate, error)
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -523,6 +524,30 @@ func TestOrgCreatedAggregates(t *testing.T) {
|
|||||||
isErr: nil,
|
isErr: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "org with domain users aggregate error",
|
||||||
|
args: args{
|
||||||
|
ctx: authz.NewMockContext("org", "user"),
|
||||||
|
aggCreator: es_models.NewAggregateCreator("test"),
|
||||||
|
org: &model.Org{
|
||||||
|
ObjectRoot: es_models.ObjectRoot{
|
||||||
|
AggregateID: "sdaf",
|
||||||
|
Sequence: 5,
|
||||||
|
},
|
||||||
|
Name: "caos",
|
||||||
|
Domains: []*model.OrgDomain{{
|
||||||
|
Domain: "caos.ch",
|
||||||
|
Verified: true,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
users: func(ctx context.Context, domain string) ([]*es_models.Aggregate, error) {
|
||||||
|
return nil, errors.ThrowInternal(nil, "id", "internal error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
isErr: errors.IsPreconditionFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "no name error",
|
name: "no name error",
|
||||||
args: args{
|
args: args{
|
||||||
@ -543,7 +568,7 @@ func TestOrgCreatedAggregates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := orgCreatedAggregates(tt.args.ctx, tt.args.aggCreator, tt.args.org)
|
got, err := orgCreatedAggregates(tt.args.ctx, tt.args.aggCreator, tt.args.org, tt.args.users)
|
||||||
if tt.res.isErr == nil && err != nil {
|
if tt.res.isErr == nil && err != nil {
|
||||||
t.Errorf("no error expected got %T: %v", err, err)
|
t.Errorf("no error expected got %T: %v", err, err)
|
||||||
}
|
}
|
||||||
@ -676,7 +701,7 @@ func TestOrgDomainVerifiedAggregates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := OrgDomainVerifiedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.org, tt.args.domain)
|
got, err := OrgDomainVerifiedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.org, tt.args.domain, nil)
|
||||||
if tt.res.isErr == nil && err != nil {
|
if tt.res.isErr == nil && err != nil {
|
||||||
t.Errorf("no error expected got %T: %v", err, err)
|
t.Errorf("no error expected got %T: %v", err, err)
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
|
||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
"github.com/caos/zitadel/internal/eventstore/models"
|
"github.com/caos/zitadel/internal/eventstore/models"
|
||||||
"github.com/caos/zitadel/internal/org/model"
|
"github.com/caos/zitadel/internal/org/model"
|
||||||
es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -22,6 +24,7 @@ type OrgDomainView struct {
|
|||||||
OrgID string `json:"-" gorm:"column:org_id;primary_key"`
|
OrgID string `json:"-" gorm:"column:org_id;primary_key"`
|
||||||
Verified bool `json:"-" gorm:"column:verified"`
|
Verified bool `json:"-" gorm:"column:verified"`
|
||||||
Primary bool `json:"-" gorm:"column:primary_domain"`
|
Primary bool `json:"-" gorm:"column:primary_domain"`
|
||||||
|
ValidationType int32 `json:"validationType" gorm:"column:validation_type"`
|
||||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||||
|
|
||||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||||
@ -34,6 +37,7 @@ func OrgDomainViewFromModel(domain *model.OrgDomainView) *OrgDomainView {
|
|||||||
Domain: domain.Domain,
|
Domain: domain.Domain,
|
||||||
Primary: domain.Primary,
|
Primary: domain.Primary,
|
||||||
Verified: domain.Verified,
|
Verified: domain.Verified,
|
||||||
|
ValidationType: int32(domain.ValidationType),
|
||||||
CreationDate: domain.CreationDate,
|
CreationDate: domain.CreationDate,
|
||||||
ChangeDate: domain.ChangeDate,
|
ChangeDate: domain.ChangeDate,
|
||||||
}
|
}
|
||||||
@ -45,6 +49,7 @@ func OrgDomainToModel(domain *OrgDomainView) *model.OrgDomainView {
|
|||||||
Domain: domain.Domain,
|
Domain: domain.Domain,
|
||||||
Primary: domain.Primary,
|
Primary: domain.Primary,
|
||||||
Verified: domain.Verified,
|
Verified: domain.Verified,
|
||||||
|
ValidationType: model.OrgDomainValidationType(domain.ValidationType),
|
||||||
CreationDate: domain.CreationDate,
|
CreationDate: domain.CreationDate,
|
||||||
ChangeDate: domain.ChangeDate,
|
ChangeDate: domain.ChangeDate,
|
||||||
}
|
}
|
||||||
@ -66,6 +71,8 @@ func (d *OrgDomainView) AppendEvent(event *models.Event) (err error) {
|
|||||||
d.setRootData(event)
|
d.setRootData(event)
|
||||||
d.CreationDate = event.CreationDate
|
d.CreationDate = event.CreationDate
|
||||||
err = d.SetData(event)
|
err = d.SetData(event)
|
||||||
|
case es_model.OrgDomainVerificationAdded:
|
||||||
|
err = d.SetData(event)
|
||||||
case es_model.OrgDomainVerified:
|
case es_model.OrgDomainVerified:
|
||||||
d.Verified = true
|
d.Verified = true
|
||||||
case es_model.OrgDomainPrimarySet:
|
case es_model.OrgDomainPrimarySet:
|
||||||
|
@ -203,6 +203,9 @@ EventTypes:
|
|||||||
removed: Organisation entfernt
|
removed: Organisation entfernt
|
||||||
domain:
|
domain:
|
||||||
added: Domäne hinzugefügt
|
added: Domäne hinzugefügt
|
||||||
|
verification:
|
||||||
|
added: Domänenverifizierung hinzugefügt
|
||||||
|
failed: Domänenverifizierung fehlgeschlagen
|
||||||
verified: Domäne verifiziert
|
verified: Domäne verifiziert
|
||||||
removed: Domäne entfernt
|
removed: Domäne entfernt
|
||||||
primary:
|
primary:
|
||||||
|
@ -203,6 +203,9 @@ EventTypes:
|
|||||||
removed: Organization removed
|
removed: Organization removed
|
||||||
domain:
|
domain:
|
||||||
added: Domain added
|
added: Domain added
|
||||||
|
verification:
|
||||||
|
added: Domain verification added
|
||||||
|
failed: Domain verification failed
|
||||||
verified: Domain verified
|
verified: Domain verified
|
||||||
removed: Domain removed
|
removed: Domain removed
|
||||||
primary:
|
primary:
|
||||||
|
@ -3,13 +3,15 @@ package eventsourcing
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
"github.com/caos/zitadel/internal/id"
|
"github.com/caos/zitadel/internal/id"
|
||||||
org_model "github.com/caos/zitadel/internal/org/model"
|
org_model "github.com/caos/zitadel/internal/org/model"
|
||||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||||
"github.com/golang/protobuf/ptypes"
|
|
||||||
|
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ type UserEventstore struct {
|
|||||||
es_int.Eventstore
|
es_int.Eventstore
|
||||||
userCache *UserCache
|
userCache *UserCache
|
||||||
idGenerator id.Generator
|
idGenerator id.Generator
|
||||||
|
defaultDomain string
|
||||||
PasswordAlg crypto.HashAlgorithm
|
PasswordAlg crypto.HashAlgorithm
|
||||||
InitializeUserCode crypto.Generator
|
InitializeUserCode crypto.Generator
|
||||||
EmailVerificationCode crypto.Generator
|
EmailVerificationCode crypto.Generator
|
||||||
@ -65,6 +68,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
|
|||||||
Eventstore: conf.Eventstore,
|
Eventstore: conf.Eventstore,
|
||||||
userCache: userCache,
|
userCache: userCache,
|
||||||
idGenerator: id.SonyFlakeGenerator,
|
idGenerator: id.SonyFlakeGenerator,
|
||||||
|
defaultDomain: systemDefaults.DefaultDomain,
|
||||||
InitializeUserCode: initCodeGen,
|
InitializeUserCode: initCodeGen,
|
||||||
EmailVerificationCode: emailVerificationCode,
|
EmailVerificationCode: emailVerificationCode,
|
||||||
PhoneVerificationCode: phoneVerificationCode,
|
PhoneVerificationCode: phoneVerificationCode,
|
||||||
@ -1084,3 +1088,32 @@ func (es *UserEventstore) SignOut(ctx context.Context, agentID string, userIDs [
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) PrepareDomainClaimed(ctx context.Context, userIDs []string) ([]*es_models.Aggregate, error) {
|
||||||
|
aggregates := make([]*es_models.Aggregate, 0)
|
||||||
|
for _, userID := range userIDs {
|
||||||
|
user, err := es.UserByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
repoUser := model.UserFromModel(user)
|
||||||
|
name, err := es.generateTemporaryLoginName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userAgg, err := DomainClaimedAggregate(ctx, es.AggregateCreator(), repoUser, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aggregates = append(aggregates, userAgg...)
|
||||||
|
}
|
||||||
|
return aggregates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) generateTemporaryLoginName() (string, error) {
|
||||||
|
id, err := es.idGenerator.Next()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s@temporary.%s", id, es.defaultDomain), nil
|
||||||
|
}
|
||||||
|
@ -55,4 +55,6 @@ const (
|
|||||||
MfaInitSkipped models.EventType = "user.mfa.init.skipped"
|
MfaInitSkipped models.EventType = "user.mfa.init.skipped"
|
||||||
|
|
||||||
SignedOut models.EventType = "user.signed.out"
|
SignedOut models.EventType = "user.signed.out"
|
||||||
|
|
||||||
|
DomainClaimed models.EventType = "user.domain.claimed"
|
||||||
)
|
)
|
||||||
|
@ -133,7 +133,8 @@ func (u *User) AppendEvent(event *es_models.Event) (err error) {
|
|||||||
switch event.Type {
|
switch event.Type {
|
||||||
case UserAdded,
|
case UserAdded,
|
||||||
UserRegistered,
|
UserRegistered,
|
||||||
UserProfileChanged:
|
UserProfileChanged,
|
||||||
|
DomainClaimed:
|
||||||
u.setData(event)
|
u.setData(event)
|
||||||
case UserDeactivated:
|
case UserDeactivated:
|
||||||
u.appendDeactivatedEvent()
|
u.appendDeactivatedEvent()
|
||||||
|
@ -190,6 +190,22 @@ func reservedUniqueUserNameAggregate(ctx context.Context, aggCreator *es_models.
|
|||||||
return aggregate.SetPrecondition(UserUserNameUniqueQuery(uniqueUserName), isEventValidation(aggregate, model.UserUserNameReserved)), nil
|
return aggregate.SetPrecondition(UserUserNameUniqueQuery(uniqueUserName), isEventValidation(aggregate, model.UserUserNameReserved)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func releasedUniqueUserNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, username string) (aggregate *es_models.Aggregate, err error) {
|
||||||
|
aggregate, err = aggCreator.NewAggregate(ctx, username, model.UserUserNameAggregate, model.UserVersion, 0)
|
||||||
|
if resourceOwner != "" {
|
||||||
|
aggregate, err = aggCreator.NewAggregate(ctx, username, model.UserUserNameAggregate, model.UserVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aggregate, err = aggregate.AppendEvent(model.UserUserNameReleased, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return aggregate.SetPrecondition(UserUserNameUniqueQuery(username), isEventValidation(aggregate, model.UserUserNameReleased)), nil
|
||||||
|
}
|
||||||
|
|
||||||
func reservedUniqueEmailAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, email string) (aggregate *es_models.Aggregate, err error) {
|
func reservedUniqueEmailAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, email string) (aggregate *es_models.Aggregate, err error) {
|
||||||
aggregate, err = aggCreator.NewAggregate(ctx, email, model.UserEmailAggregate, model.UserVersion, 0)
|
aggregate, err = aggCreator.NewAggregate(ctx, email, model.UserEmailAggregate, model.UserVersion, 0)
|
||||||
if resourceOwner != "" {
|
if resourceOwner != "" {
|
||||||
@ -656,6 +672,30 @@ func SignOutAggregates(aggCreator *es_models.AggregateCreator, existingUsers []*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DomainClaimedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existingUser *model.User, tempName string) ([]*es_models.Aggregate, error) {
|
||||||
|
aggregates := make([]*es_models.Aggregate, 3)
|
||||||
|
userAggregate, err := UserAggregateOverwriteContext(ctx, aggCreator, existingUser, existingUser.ResourceOwner, existingUser.AggregateID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userAggregate, err = userAggregate.AppendEvent(model.DomainClaimed, map[string]interface{}{"userName": tempName})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aggregates[0] = userAggregate
|
||||||
|
releasedUniqueAggregate, err := releasedUniqueUserNameAggregate(ctx, aggCreator, existingUser.ResourceOwner, existingUser.UserName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aggregates[1] = releasedUniqueAggregate
|
||||||
|
reservedUniqueAggregate, err := reservedUniqueUserNameAggregate(ctx, aggCreator, existingUser.ResourceOwner, tempName, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aggregates[2] = reservedUniqueAggregate
|
||||||
|
return aggregates, nil
|
||||||
|
}
|
||||||
|
|
||||||
func isEventValidation(aggregate *es_models.Aggregate, eventType es_models.EventType) func(...*es_models.Event) error {
|
func isEventValidation(aggregate *es_models.Aggregate, eventType es_models.EventType) func(...*es_models.Event) error {
|
||||||
return func(events ...*es_models.Event) error {
|
return func(events ...*es_models.Event) error {
|
||||||
if len(events) == 0 {
|
if len(events) == 0 {
|
||||||
@ -678,6 +718,7 @@ func addUserNameValidation(userName string) func(...*es_models.Event) error {
|
|||||||
case org_es_model.OrgDomainAdded:
|
case org_es_model.OrgDomainAdded:
|
||||||
domain := new(org_es_model.OrgDomain)
|
domain := new(org_es_model.OrgDomain)
|
||||||
domain.SetData(event)
|
domain.SetData(event)
|
||||||
|
domains = append(domains, domain)
|
||||||
case org_es_model.OrgDomainVerified:
|
case org_es_model.OrgDomainVerified:
|
||||||
domain := new(org_es_model.OrgDomain)
|
domain := new(org_es_model.OrgDomain)
|
||||||
domain.SetData(event)
|
domain.SetData(event)
|
||||||
@ -694,6 +735,7 @@ func addUserNameValidation(userName string) func(...*es_models.Event) error {
|
|||||||
domains[i] = domains[len(domains)-1]
|
domains[i] = domains[len(domains)-1]
|
||||||
domains[len(domains)-1] = nil
|
domains[len(domains)-1] = nil
|
||||||
domains = domains[:len(domains)-1]
|
domains = domains[:len(domains)-1]
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,8 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) {
|
|||||||
case es_model.UserPasswordChanged:
|
case es_model.UserPasswordChanged:
|
||||||
err = u.setPasswordData(event)
|
err = u.setPasswordData(event)
|
||||||
case es_model.UserProfileChanged,
|
case es_model.UserProfileChanged,
|
||||||
es_model.UserAddressChanged:
|
es_model.UserAddressChanged,
|
||||||
|
es_model.DomainClaimed:
|
||||||
err = u.setData(event)
|
err = u.setData(event)
|
||||||
case es_model.UserEmailChanged:
|
case es_model.UserEmailChanged:
|
||||||
u.IsEmailVerified = false
|
u.IsEmailVerified = false
|
||||||
|
@ -2,6 +2,7 @@ package view
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/caos/zitadel/internal/view/repository"
|
"github.com/caos/zitadel/internal/view/repository"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||||
@ -59,6 +60,20 @@ func UsersByOrgID(db *gorm.DB, table, orgID string) ([]*model.UserView, error) {
|
|||||||
return users, err
|
return users, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserIDsByDomain(db *gorm.DB, table, domain string) ([]string, error) {
|
||||||
|
users := make([]string, 0)
|
||||||
|
orgIDQuery := &usr_model.UserSearchQuery{
|
||||||
|
Key: usr_model.UserSearchKeyUserName,
|
||||||
|
Method: global_model.SearchMethodEndsWithIgnoreCase,
|
||||||
|
Value: "%" + domain,
|
||||||
|
}
|
||||||
|
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{
|
||||||
|
Queries: []*usr_model.UserSearchQuery{orgIDQuery},
|
||||||
|
})
|
||||||
|
_, err := query(db, &users)
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
|
||||||
func SearchUsers(db *gorm.DB, table string, req *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
|
func SearchUsers(db *gorm.DB, table string, req *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
|
||||||
users := make([]*model.UserView, 0)
|
users := make([]*model.UserView, 0)
|
||||||
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
|
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
|
||||||
|
@ -85,6 +85,18 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
|
|||||||
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eidus", "Starts with ignore case only possible for strings")
|
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-eidus", "Starts with ignore case only possible for strings")
|
||||||
}
|
}
|
||||||
query = query.Where("LOWER("+column+") LIKE LOWER(?)", valueText+"%")
|
query = query.Where("LOWER("+column+") LIKE LOWER(?)", valueText+"%")
|
||||||
|
case model.SearchMethodEndsWith:
|
||||||
|
valueText, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-Hswd3", "Ends with only possible for strings")
|
||||||
|
}
|
||||||
|
query = query.Where(column+" LIKE ?", "%"+valueText)
|
||||||
|
case model.SearchMethodEndsWithIgnoreCase:
|
||||||
|
valueText, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, caos_errs.ThrowInvalidArgument(nil, "VIEW-dAG31", "Ends with ignore case only possible for strings")
|
||||||
|
}
|
||||||
|
query = query.Where("LOWER("+column+") LIKE LOWER(?)", "%"+valueText)
|
||||||
case model.SearchMethodContains:
|
case model.SearchMethodContains:
|
||||||
valueText, ok := value.(string)
|
valueText, ok := value.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
44
migrations/cockroach/V1.5__orgdomain_validationtype.sql
Normal file
44
migrations/cockroach/V1.5__orgdomain_validationtype.sql
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE management.org_domains ADD COLUMN validation_type SMALLINT;
|
||||||
|
|
||||||
|
CREATE TABLE adminapi.users (
|
||||||
|
id TEXT,
|
||||||
|
|
||||||
|
creation_date TIMESTAMPTZ,
|
||||||
|
change_date TIMESTAMPTZ,
|
||||||
|
|
||||||
|
resource_owner TEXT,
|
||||||
|
user_state SMALLINT,
|
||||||
|
last_login TIMESTAMPTZ,
|
||||||
|
password_change TIMESTAMPTZ,
|
||||||
|
user_name TEXT,
|
||||||
|
login_names TEXT ARRAY,
|
||||||
|
preferred_login_name TEXT,
|
||||||
|
first_name TEXT,
|
||||||
|
last_name TEXT,
|
||||||
|
nick_Name TEXT,
|
||||||
|
display_name TEXT,
|
||||||
|
preferred_language TEXT,
|
||||||
|
gender SMALLINT,
|
||||||
|
email TEXT,
|
||||||
|
is_email_verified BOOLEAN,
|
||||||
|
phone TEXT,
|
||||||
|
is_phone_verified BOOLEAN,
|
||||||
|
country TEXT,
|
||||||
|
locality TEXT,
|
||||||
|
postal_code TEXT,
|
||||||
|
region TEXT,
|
||||||
|
street_address TEXT,
|
||||||
|
otp_state SMALLINT,
|
||||||
|
sequence BIGINT,
|
||||||
|
password_set BOOLEAN,
|
||||||
|
password_change_required BOOLEAN,
|
||||||
|
mfa_max_set_up SMALLINT,
|
||||||
|
mfa_init_skipped TIMESTAMPTZ,
|
||||||
|
init_required BOOLEAN,
|
||||||
|
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1264,7 +1264,7 @@ message Iam {
|
|||||||
message ChangeRequest {
|
message ChangeRequest {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string sec_id = 2;
|
string sec_id = 2;
|
||||||
uint64 limit= 3;
|
uint64 limit = 3;
|
||||||
uint64 sequence_offset = 4;
|
uint64 sequence_offset = 4;
|
||||||
bool asc = 5;
|
bool asc = 5;
|
||||||
}
|
}
|
||||||
@ -1360,7 +1360,7 @@ enum UserState {
|
|||||||
USERSTATE_DELETED = 3;
|
USERSTATE_DELETED = 3;
|
||||||
USERSTATE_LOCKED = 4;
|
USERSTATE_LOCKED = 4;
|
||||||
USERSTATE_SUSPEND = 5;
|
USERSTATE_SUSPEND = 5;
|
||||||
USERSTATE_INITIAL= 6;
|
USERSTATE_INITIAL = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Gender {
|
enum Gender {
|
||||||
@ -1781,6 +1781,7 @@ message OrgDomainView {
|
|||||||
bool verified = 5;
|
bool verified = 5;
|
||||||
bool primary = 6;
|
bool primary = 6;
|
||||||
uint64 sequence = 7;
|
uint64 sequence = 7;
|
||||||
|
OrgDomainValidationType validation_type = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddOrgDomainRequest {
|
message AddOrgDomainRequest {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user