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
41 changed files with 15945 additions and 16717 deletions

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