zitadel/internal/command/policy_org_model.go
Livio Spring 48f535afa1
fix: remove domain and user correctly in DomainPolicyUsernamesWriteModel (#4850)
fix: remove Domain and User correctly in DomainPolicyUsernamesWriteModel
2022-12-08 08:11:49 +01:00

182 lines
5.6 KiB
Go

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 {
eventstore.WriteModel
UserLoginMustBeDomain bool
ValidateOrgDomains bool
SMTPSenderAddressMatchesInstanceDomain bool
State domain.PolicyState
}
func (wm *PolicyDomainWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.DomainPolicyAddedEvent:
wm.UserLoginMustBeDomain = e.UserLoginMustBeDomain
wm.ValidateOrgDomains = e.ValidateOrgDomains
wm.SMTPSenderAddressMatchesInstanceDomain = e.SMTPSenderAddressMatchesInstanceDomain
wm.State = domain.PolicyStateActive
case *policy.DomainPolicyChangedEvent:
if e.UserLoginMustBeDomain != nil {
wm.UserLoginMustBeDomain = *e.UserLoginMustBeDomain
}
if e.ValidateOrgDomains != nil {
wm.ValidateOrgDomains = *e.ValidateOrgDomains
}
if e.SMTPSenderAddressMatchesInstanceDomain != nil {
wm.SMTPSenderAddressMatchesInstanceDomain = *e.SMTPSenderAddressMatchesInstanceDomain
}
case *policy.DomainPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}
}
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 {
wm.VerifiedDomains[i] = wm.VerifiedDomains[len(wm.VerifiedDomains)-1]
wm.VerifiedDomains[len(wm.VerifiedDomains)-1] = ""
wm.VerifiedDomains = wm.VerifiedDomains[:len(wm.VerifiedDomains)-1]
return
}
}
}
func (wm *DomainPolicyUsernamesWriteModel) removeUser(userID string) {
for i, user := range wm.Users {
if user.id == userID {
wm.Users[i] = wm.Users[len(wm.Users)-1]
wm.Users[len(wm.Users)-1] = nil
wm.Users = wm.Users[:len(wm.Users)-1]
return
}
}
}
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
}