mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:47:32 +00:00
fix: handle UserLoginMustBeDomain changes correctly (#4765)
* fix: handle UserLoginMustBeDomain changes correctly * fix: remove verified domains (and not only primary) as suffix * fix: ensure testability by changing map to slice * cleanup * reduce complexity of DomainPolicyUsernamesWriteModel.Reduce() * add test for removed org policy
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/org"
|
||||
"github.com/zitadel/zitadel/internal/repository/policy"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
type PolicyDomainWriteModel struct {
|
||||
@@ -39,3 +44,138 @@ func (wm *PolicyDomainWriteModel) Reduce() error {
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
type DomainPolicyUsernamesWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
PrimaryDomain string
|
||||
VerifiedDomains []string
|
||||
Users []*domainPolicyUsers
|
||||
}
|
||||
|
||||
type domainPolicyUsers struct {
|
||||
id string
|
||||
username string
|
||||
}
|
||||
|
||||
func NewDomainPolicyUsernamesWriteModel(orgID string) *DomainPolicyUsernamesWriteModel {
|
||||
return &DomainPolicyUsernamesWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
Users: make([]*domainPolicyUsers, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
wm.WriteModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *org.DomainVerifiedEvent:
|
||||
wm.VerifiedDomains = append(wm.VerifiedDomains, e.Domain)
|
||||
case *org.DomainRemovedEvent:
|
||||
wm.removeDomain(e.Domain)
|
||||
case *org.DomainPrimarySetEvent:
|
||||
wm.PrimaryDomain = e.Domain
|
||||
case *user.HumanAddedEvent:
|
||||
wm.Users = append(wm.Users, &domainPolicyUsers{id: e.Aggregate().ID, username: e.UserName})
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Users = append(wm.Users, &domainPolicyUsers{id: e.Aggregate().ID, username: e.UserName})
|
||||
case *user.MachineAddedEvent:
|
||||
wm.Users = append(wm.Users, &domainPolicyUsers{id: e.Aggregate().ID, username: e.UserName})
|
||||
case *user.UsernameChangedEvent:
|
||||
for _, user := range wm.Users {
|
||||
if user.id == e.Aggregate().ID {
|
||||
user.username = e.UserName
|
||||
break
|
||||
}
|
||||
}
|
||||
case *user.DomainClaimedEvent:
|
||||
for _, user := range wm.Users {
|
||||
if user.id == e.Aggregate().ID {
|
||||
user.username = e.UserName
|
||||
break
|
||||
}
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.removeUser(e.Aggregate().ID)
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) removeDomain(domain string) {
|
||||
for i, verifiedDomain := range wm.VerifiedDomains {
|
||||
if verifiedDomain != domain {
|
||||
continue
|
||||
}
|
||||
wm.VerifiedDomains[i] = wm.VerifiedDomains[len(wm.VerifiedDomains)-1]
|
||||
wm.VerifiedDomains[len(wm.VerifiedDomains)-1] = ""
|
||||
wm.VerifiedDomains = wm.VerifiedDomains[:len(wm.VerifiedDomains)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) removeUser(userID string) {
|
||||
for i, user := range wm.Users {
|
||||
if user.id != userID {
|
||||
continue
|
||||
}
|
||||
wm.Users[i] = wm.Users[len(wm.Users)-1]
|
||||
wm.Users[len(wm.Users)-1] = nil
|
||||
wm.Users = wm.Users[len(wm.Users)-1:]
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType, user.AggregateType).
|
||||
EventTypes(
|
||||
org.OrgDomainVerifiedEventType,
|
||||
org.OrgDomainRemovedEventType,
|
||||
org.OrgDomainPrimarySetEventType,
|
||||
user.HumanAddedType,
|
||||
user.HumanRegisteredType,
|
||||
user.MachineAddedEventType,
|
||||
user.UserUserNameChangedType,
|
||||
user.UserDomainClaimedType,
|
||||
user.UserRemovedType,
|
||||
).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) NewUsernameChangedEvents(ctx context.Context, userLoginMustBeDomain bool) []eventstore.Command {
|
||||
events := make([]eventstore.Command, 0, len(wm.Users))
|
||||
for _, changeUser := range wm.Users {
|
||||
events = append(events, user.NewUsernameChangedEvent(ctx,
|
||||
&user.NewAggregate(changeUser.id, wm.ResourceOwner).Aggregate,
|
||||
changeUser.username,
|
||||
wm.newUsername(changeUser.username, userLoginMustBeDomain),
|
||||
userLoginMustBeDomain,
|
||||
user.UsernameChangedEventWithPolicyChange()),
|
||||
)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func (wm *DomainPolicyUsernamesWriteModel) newUsername(username string, userLoginMustBeDomain bool) string {
|
||||
if !userLoginMustBeDomain {
|
||||
// if the UserLoginMustBeDomain will be false, then it's currently true
|
||||
// which means the usernames must be suffixed to ensure their uniqueness
|
||||
// and the preferred login name remains the same
|
||||
return username + "@" + wm.PrimaryDomain
|
||||
}
|
||||
// the UserLoginMustBeDomain is currently false
|
||||
// which means the usernames might already be suffixed by a verified domain
|
||||
// so let's remove a potential duplicate suffix
|
||||
for _, verifiedDomain := range wm.VerifiedDomains {
|
||||
if index := strings.LastIndex(username, "@"+verifiedDomain); index > 0 {
|
||||
return username[:index]
|
||||
}
|
||||
}
|
||||
return username
|
||||
}
|
||||
|
Reference in New Issue
Block a user