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:
Livio Amstutz 2020-08-18 08:57:16 +02:00 committed by GitHub
parent 406924bed8
commit 1a00faf132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 15945 additions and 16717 deletions

View File

@ -1,5 +1,6 @@
SystemDefaults:
DefaultLanguage: 'de'
DefaultDomain: $ZITADEL_DEFAULT_DOMAIN
ZitadelDocs:
Issuer: $ZITADEL_ISSUER
DiscoveryEndpoint: '$ZITADEL_ISSUER/.well-known/openid-configuration'

View File

@ -5,7 +5,10 @@
<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
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
</div>

View File

@ -119,7 +119,18 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
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);
});
}

View File

@ -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_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_VALIDATION_ONGOING":"Ein Verifikationstoken wurde bereits angefragt. Klicke auf dem Button um dieses zu verifizieren!",
"DOWNLOAD_FILE":"Datei download",
"SELECTORGTOOLTIP":"Wähle diese Organisation",
"PRIMARYDOMAIN":"Primäre Domain",

View File

@ -285,6 +285,7 @@
"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_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",
"SELECTORGTOOLTIP":"Select this organisation",
"PRIMARYDOMAIN":"Primary Domain",

View File

@ -2,11 +2,13 @@ package eventstore
import (
"context"
"github.com/caos/logging"
admin_model "github.com/caos/zitadel/internal/admin/model"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/eventstore"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/sdk"
org_model "github.com/caos/zitadel/internal/org/model"
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 {
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 {
return nil, err
}

View File

@ -5,7 +5,9 @@ import (
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/eventstore"
"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"
)
@ -24,12 +26,14 @@ type handler struct {
type EventstoreRepos struct {
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{
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
&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},
}
}

View 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)
}

View File

@ -86,7 +86,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, r
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
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{
spooler: spool,

View File

@ -211,7 +211,7 @@ func (setUp *initializer) org(ctx context.Context, org types.Org) (*org_model.Or
Name: org.Name,
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) {

View File

@ -21,7 +21,7 @@ func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sq
Eventstore: es,
Locker: &locker{dbClient: sql},
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.Start()

View 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)
}

View File

@ -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 {
return &org_model.OrgDomain{Domain: domain.Domain}
}
@ -119,12 +130,13 @@ func orgDomainViewFromModel(domain *org_model.OrgDomainView) *management.OrgDoma
logging.Log("GRPC-8iSji").OnError(err).Debug("unable to get timestamp from time")
return &management.OrgDomainView{
ChangeDate: changeDate,
CreationDate: creationDate,
OrgId: domain.OrgID,
Domain: domain.Domain,
Verified: domain.Verified,
Primary: domain.Primary,
ChangeDate: changeDate,
CreationDate: creationDate,
OrgId: domain.OrgID,
Domain: domain.Domain,
Verified: domain.Verified,
Primary: domain.Primary,
ValidationType: orgDomainValidationTypeFromModel(domain.ValidationType),
}
}

View File

@ -2,9 +2,12 @@ package eventstore
import (
"context"
"github.com/caos/logging"
auth_model "github.com/caos/zitadel/internal/auth/model"
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"
org_model "github.com/caos/zitadel/internal/org/model"
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 {
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 {
return nil, err
}

View File

@ -27,12 +27,12 @@ const (
userTable = "auth.users"
)
func (p *User) ViewModel() string {
func (u *User) ViewModel() string {
return userTable
}
func (p *User) EventQuery() (*models.SearchQuery, error) {
sequence, err := p.view.GetLatestUserSequence()
func (u *User) EventQuery() (*models.SearchQuery, error) {
sequence, err := u.view.GetLatestUserSequence()
if err != nil {
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)
switch event.Type {
case es_model.UserAdded,
@ -61,7 +61,7 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
if err != nil {
return err
}
p.fillLoginNames(user)
u.fillLoginNames(user)
case es_model.UserProfileChanged,
es_model.UserEmailChanged,
es_model.UserEmailVerified,
@ -78,20 +78,30 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
es_model.MfaOtpRemoved,
es_model.MfaInitSkipped,
es_model.UserPasswordChanged:
user, err = p.view.UserByID(event.AggregateID)
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 = p.view.DeleteUser(event.AggregateID, event.Sequence)
err = u.view.DeleteUser(event.AggregateID, event.Sequence)
default:
return p.view.ProcessedUserSequence(event.Sequence)
return u.view.ProcessedUserSequence(event.Sequence)
}
if err != nil {
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) {
@ -172,7 +182,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error {
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")
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)
}

View File

@ -26,6 +26,11 @@ func (v *View) UserByLoginName(loginName string) (*model.UserView, error) {
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) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, uint64, error) {
return view.SearchUsers(v.Db, userTable, request)
}

View File

@ -15,6 +15,7 @@ import (
type SystemDefaults struct {
DefaultLanguage language.Tag
DefaultDomain string
ZitadelDocs ZitadelDocs
SecretGenerators SecretGenerators
UserVerificationKey *crypto.KeyConfig

View File

@ -2,11 +2,12 @@ package eventstore
import (
"context"
"strings"
"github.com/caos/logging"
"strings"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/sdk"
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
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"
"github.com/caos/zitadel/internal/org/repository/view/model"
usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
)
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) {
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 {
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 {
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 {

View File

@ -45,7 +45,8 @@ func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) {
switch event.Type {
case model.OrgDomainAdded:
err = domain.AppendEvent(event)
case model.OrgDomainVerified:
case model.OrgDomainVerified,
model.OrgDomainVerificationAdded:
err = domain.SetData(event)
if err != nil {
return err

View File

@ -26,12 +26,12 @@ const (
userTable = "management.users"
)
func (p *User) ViewModel() string {
func (u *User) ViewModel() string {
return userTable
}
func (p *User) EventQuery() (*models.SearchQuery, error) {
sequence, err := p.view.GetLatestUserSequence()
func (u *User) EventQuery() (*models.SearchQuery, error) {
sequence, err := u.view.GetLatestUserSequence()
if err != nil {
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)
switch event.Type {
case es_model.UserAdded,
@ -60,7 +60,7 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
if err != nil {
return err
}
err = p.fillLoginNames(user)
err = u.fillLoginNames(user)
case es_model.UserProfileChanged,
es_model.UserEmailChanged,
es_model.UserEmailVerified,
@ -75,20 +75,30 @@ func (p *User) ProcessUser(event *models.Event) (err error) {
es_model.MfaOtpAdded,
es_model.MfaOtpVerified,
es_model.MfaOtpRemoved:
user, err = p.view.UserByID(event.AggregateID)
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 = p.view.DeleteUser(event.AggregateID, event.Sequence)
err = u.view.DeleteUser(event.AggregateID, event.Sequence)
default:
return p.view.ProcessedUserSequence(event.Sequence)
return u.view.ProcessedUserSequence(event.Sequence)
}
if err != nil {
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) {
@ -161,7 +171,7 @@ func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
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")
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)
}

View File

@ -27,6 +27,10 @@ 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)
}

View File

@ -14,4 +14,6 @@ const (
SearchMethodLessThan
SearchMethodIsOneOf
SearchMethodListContains
SearchMethodEndsWith
SearchMethodEndsWithIgnoreCase
)

View File

@ -1,17 +1,19 @@
package model
import (
"github.com/caos/zitadel/internal/model"
"time"
"github.com/caos/zitadel/internal/model"
)
type OrgDomainView struct {
OrgID string
CreationDate time.Time
ChangeDate time.Time
Domain string
Primary bool
Verified bool
OrgID string
CreationDate time.Time
ChangeDate time.Time
Domain string
Primary bool
Verified bool
ValidationType OrgDomainValidationType
}
type OrgDomainSearchRequest struct {

View File

@ -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() {
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
org := model.OrgFromModel(orgModel)
aggregates, err := orgCreatedAggregates(ctx, es.AggregateCreator(), org)
aggregates, err := orgCreatedAggregates(ctx, es.AggregateCreator(), org, users)
return org, aggregates, err
}
func (es *OrgEventstore) CreateOrg(ctx context.Context, orgModel *org_model.Org) (*org_model.Org, error) {
org, aggregates, err := es.PrepareCreateOrg(ctx, orgModel)
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, users)
err = es_sdk.PushAggregates(ctx, es.PushAggregates, org.AppendEvents, aggregates...)
if err != nil {
return nil, err
@ -222,7 +222,7 @@ func (es *OrgEventstore) GenerateOrgDomainValidation(ctx context.Context, domain
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() {
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()
err = es.verificationValidator(d.Domain, validationCode, validationCode, checkType)
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 {
return err
}

View File

@ -578,6 +578,7 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
type args struct {
ctx context.Context
domain *org_model.OrgDomain
users func(ctx context.Context, domain string) ([]*es_models.Aggregate, error)
}
tests := []struct {
name string
@ -682,6 +683,26 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
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",
fields: fields{Eventstore: newTestEventstore(t).
@ -719,7 +740,7 @@ func TestOrgEventstore_ValidateOrgDomain(t *testing.T) {
}
for _, tt := range tests {
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 {
t.Errorf("no error expected got:%T %v", err, err)
}

View File

@ -2,6 +2,7 @@ package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
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)
}
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 {
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
}
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 {
return nil, err
}
@ -68,22 +69,18 @@ func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCr
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 {
orgAggregate, err := orgAggregate.AppendEvent(model.OrgDomainAdded, domain)
if err != nil {
return nil, err
}
if domain.Verified {
domainAggregate, err := reservedUniqueDomainAggregate(ctx, aggCreator, org.AggregateID, domain.Domain)
if err != nil {
return nil, err
}
aggregates = append(aggregates, domainAggregate)
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainVerified, domain)
domainAggregates, err := OrgDomainVerifiedAggregate(ctx, aggCreator, org, domain, users)
if err != nil {
return nil, err
}
aggregates = append(aggregates, domainAggregates...)
}
if domain.Primary {
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainPrimarySet, domain)
@ -182,7 +179,6 @@ func reservedUniqueDomainAggregate(ctx context.Context, aggCreator *es_models.Ag
if err != nil {
return nil, err
}
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 {
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
}
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
}

View File

@ -466,6 +466,7 @@ func TestOrgCreatedAggregates(t *testing.T) {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
users func(ctx context.Context, domain string) ([]*es_models.Aggregate, error)
}
tests := []struct {
name string
@ -523,6 +524,30 @@ func TestOrgCreatedAggregates(t *testing.T) {
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",
args: args{
@ -543,7 +568,7 @@ func TestOrgCreatedAggregates(t *testing.T) {
}
for _, tt := range tests {
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 {
t.Errorf("no error expected got %T: %v", err, err)
}
@ -676,7 +701,7 @@ func TestOrgDomainVerifiedAggregates(t *testing.T) {
}
for _, tt := range tests {
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 {
t.Errorf("no error expected got %T: %v", err, err)
}

View File

@ -2,12 +2,14 @@ package model
import (
"encoding/json"
"time"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/model"
es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"time"
)
const (
@ -18,11 +20,12 @@ const (
)
type OrgDomainView struct {
Domain string `json:"domain" gorm:"column:domain;primary_key"`
OrgID string `json:"-" gorm:"column:org_id;primary_key"`
Verified bool `json:"-" gorm:"column:verified"`
Primary bool `json:"-" gorm:"column:primary_domain"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
Domain string `json:"domain" gorm:"column:domain;primary_key"`
OrgID string `json:"-" gorm:"column:org_id;primary_key"`
Verified bool `json:"-" gorm:"column:verified"`
Primary bool `json:"-" gorm:"column:primary_domain"`
ValidationType int32 `json:"validationType" gorm:"column:validation_type"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
@ -30,23 +33,25 @@ type OrgDomainView struct {
func OrgDomainViewFromModel(domain *model.OrgDomainView) *OrgDomainView {
return &OrgDomainView{
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
ValidationType: int32(domain.ValidationType),
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
}
}
func OrgDomainToModel(domain *OrgDomainView) *model.OrgDomainView {
return &model.OrgDomainView{
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
ValidationType: model.OrgDomainValidationType(domain.ValidationType),
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
}
}
@ -66,6 +71,8 @@ func (d *OrgDomainView) AppendEvent(event *models.Event) (err error) {
d.setRootData(event)
d.CreationDate = event.CreationDate
err = d.SetData(event)
case es_model.OrgDomainVerificationAdded:
err = d.SetData(event)
case es_model.OrgDomainVerified:
d.Verified = true
case es_model.OrgDomainPrimarySet:

View File

@ -203,6 +203,9 @@ EventTypes:
removed: Organisation entfernt
domain:
added: Domäne hinzugefügt
verification:
added: Domänenverifizierung hinzugefügt
failed: Domänenverifizierung fehlgeschlagen
verified: Domäne verifiziert
removed: Domäne entfernt
primary:

View File

@ -203,6 +203,9 @@ EventTypes:
removed: Organization removed
domain:
added: Domain added
verification:
added: Domain verification added
failed: Domain verification failed
verified: Domain verified
removed: Domain removed
primary:

View File

@ -3,13 +3,15 @@ package eventsourcing
import (
"context"
"encoding/json"
"fmt"
"github.com/caos/logging"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/id"
org_model "github.com/caos/zitadel/internal/org/model"
policy_model "github.com/caos/zitadel/internal/policy/model"
"github.com/golang/protobuf/ptypes"
"github.com/pquerna/otp/totp"
@ -30,6 +32,7 @@ type UserEventstore struct {
es_int.Eventstore
userCache *UserCache
idGenerator id.Generator
defaultDomain string
PasswordAlg crypto.HashAlgorithm
InitializeUserCode crypto.Generator
EmailVerificationCode crypto.Generator
@ -65,6 +68,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
Eventstore: conf.Eventstore,
userCache: userCache,
idGenerator: id.SonyFlakeGenerator,
defaultDomain: systemDefaults.DefaultDomain,
InitializeUserCode: initCodeGen,
EmailVerificationCode: emailVerificationCode,
PhoneVerificationCode: phoneVerificationCode,
@ -1084,3 +1088,32 @@ func (es *UserEventstore) SignOut(ctx context.Context, agentID string, userIDs [
}
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
}

View File

@ -55,4 +55,6 @@ const (
MfaInitSkipped models.EventType = "user.mfa.init.skipped"
SignedOut models.EventType = "user.signed.out"
DomainClaimed models.EventType = "user.domain.claimed"
)

View File

@ -133,7 +133,8 @@ func (u *User) AppendEvent(event *es_models.Event) (err error) {
switch event.Type {
case UserAdded,
UserRegistered,
UserProfileChanged:
UserProfileChanged,
DomainClaimed:
u.setData(event)
case UserDeactivated:
u.appendDeactivatedEvent()

View File

@ -190,6 +190,22 @@ func reservedUniqueUserNameAggregate(ctx context.Context, aggCreator *es_models.
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) {
aggregate, err = aggCreator.NewAggregate(ctx, email, model.UserEmailAggregate, model.UserVersion, 0)
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 {
return func(events ...*es_models.Event) error {
if len(events) == 0 {
@ -678,6 +718,7 @@ func addUserNameValidation(userName string) func(...*es_models.Event) error {
case org_es_model.OrgDomainAdded:
domain := new(org_es_model.OrgDomain)
domain.SetData(event)
domains = append(domains, domain)
case org_es_model.OrgDomainVerified:
domain := new(org_es_model.OrgDomain)
domain.SetData(event)
@ -694,6 +735,7 @@ func addUserNameValidation(userName string) func(...*es_models.Event) error {
domains[i] = domains[len(domains)-1]
domains[len(domains)-1] = nil
domains = domains[:len(domains)-1]
break
}
}
}

View File

@ -181,7 +181,8 @@ func (u *UserView) AppendEvent(event *models.Event) (err error) {
case es_model.UserPasswordChanged:
err = u.setPasswordData(event)
case es_model.UserProfileChanged,
es_model.UserAddressChanged:
es_model.UserAddressChanged,
es_model.DomainClaimed:
err = u.setData(event)
case es_model.UserEmailChanged:
u.IsEmailVerified = false

View File

@ -2,6 +2,7 @@ package view
import (
"github.com/caos/zitadel/internal/view/repository"
"github.com/jinzhu/gorm"
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
}
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) {
users := make([]*model.UserView, 0)
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})

View File

@ -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")
}
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:
valueText, ok := value.(string)
if !ok {

View 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

File diff suppressed because it is too large Load Diff