mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:47:33 +00:00
feat: multiple domains (#188)
* check uniqueness on create and register user * change user email, reserve release unique email * usergrant unique aggregate * usergrant uniqueness * validate UserGrant * fix tests * domain is set on username in all orgs * domain in admin * org domain sql * zitadel domain org name * org domains * org iam policy * default org iam policy * SETUP * load login names * login by login name * login name * fix: merge master * fix: merge master * Update internal/user/repository/eventsourcing/user.go Co-authored-by: Livio Amstutz <livio.a@gmail.com> * fix: fix unique domains * fix: rename env variable Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
120
internal/org/repository/eventsourcing/model/domain.go
Normal file
120
internal/org/repository/eventsourcing/model/domain.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/org/model"
|
||||
)
|
||||
|
||||
type OrgDomain struct {
|
||||
es_models.ObjectRoot `json:"-"`
|
||||
|
||||
Domain string `json:"domain"`
|
||||
Verified bool `json:"-"`
|
||||
Primary bool `json:"-"`
|
||||
}
|
||||
|
||||
func GetDomain(domains []*OrgDomain, domain string) (int, *OrgDomain) {
|
||||
for i, d := range domains {
|
||||
if d.Domain == domain {
|
||||
return i, d
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (o *Org) appendAddDomainEvent(event *es_models.Event) error {
|
||||
domain := new(OrgDomain)
|
||||
err := domain.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
domain.ObjectRoot.CreationDate = event.CreationDate
|
||||
o.Domains = append(o.Domains, domain)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Org) appendRemoveDomainEvent(event *es_models.Event) error {
|
||||
domain := new(OrgDomain)
|
||||
err := domain.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i, r := GetDomain(o.Domains, domain.Domain); r != nil {
|
||||
o.Domains[i] = o.Domains[len(o.Domains)-1]
|
||||
o.Domains[len(o.Domains)-1] = nil
|
||||
o.Domains = o.Domains[:len(o.Domains)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Org) appendVerifyDomainEvent(event *es_models.Event) error {
|
||||
domain := new(OrgDomain)
|
||||
err := domain.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i, d := GetDomain(o.Domains, domain.Domain); d != nil {
|
||||
d.Verified = true
|
||||
o.Domains[i] = d
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Org) appendPrimaryDomainEvent(event *es_models.Event) error {
|
||||
domain := new(OrgDomain)
|
||||
err := domain.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range o.Domains {
|
||||
d.Primary = false
|
||||
if d.Domain == domain.Domain {
|
||||
d.Primary = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *OrgDomain) SetData(event *es_models.Event) error {
|
||||
err := json.Unmarshal(event.Data, m)
|
||||
if err != nil {
|
||||
return errors.ThrowInternal(err, "EVENT-Hz7Mb", "unable to unmarshal data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func OrgDomainsFromModel(domains []*model.OrgDomain) []*OrgDomain {
|
||||
convertedDomainss := make([]*OrgDomain, len(domains))
|
||||
for i, m := range domains {
|
||||
convertedDomainss[i] = OrgDomainFromModel(m)
|
||||
}
|
||||
return convertedDomainss
|
||||
}
|
||||
|
||||
func OrgDomainFromModel(domain *model.OrgDomain) *OrgDomain {
|
||||
return &OrgDomain{
|
||||
ObjectRoot: domain.ObjectRoot,
|
||||
Domain: domain.Domain,
|
||||
Verified: domain.Verified,
|
||||
Primary: domain.Primary,
|
||||
}
|
||||
}
|
||||
|
||||
func OrgDomainsToModel(domains []*OrgDomain) []*model.OrgDomain {
|
||||
convertedDomains := make([]*model.OrgDomain, len(domains))
|
||||
for i, m := range domains {
|
||||
convertedDomains[i] = OrgDomainToModel(m)
|
||||
}
|
||||
return convertedDomains
|
||||
}
|
||||
|
||||
func OrgDomainToModel(domain *OrgDomain) *model.OrgDomain {
|
||||
return &model.OrgDomain{
|
||||
ObjectRoot: domain.ObjectRoot,
|
||||
Domain: domain.Domain,
|
||||
Verified: domain.Verified,
|
||||
Primary: domain.Primary,
|
||||
}
|
||||
}
|
@@ -14,33 +14,42 @@ const (
|
||||
type Org struct {
|
||||
es_models.ObjectRoot `json:"-"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
Name string `json:"name,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
|
||||
Members []*OrgMember `json:"-"`
|
||||
Domains []*OrgDomain `json:"-"`
|
||||
Members []*OrgMember `json:"-"`
|
||||
OrgIamPolicy *OrgIamPolicy `json:"-"`
|
||||
}
|
||||
|
||||
func OrgFromModel(org *org_model.Org) *Org {
|
||||
members := OrgMembersFromModel(org.Members)
|
||||
|
||||
return &Org{
|
||||
domains := OrgDomainsFromModel(org.Domains)
|
||||
converted := &Org{
|
||||
ObjectRoot: org.ObjectRoot,
|
||||
Domain: org.Domain,
|
||||
Name: org.Name,
|
||||
State: int32(org.State),
|
||||
Domains: domains,
|
||||
Members: members,
|
||||
}
|
||||
if org.OrgIamPolicy != nil {
|
||||
converted.OrgIamPolicy = OrgIamPolicyFromModel(org.OrgIamPolicy)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func OrgToModel(org *Org) *org_model.Org {
|
||||
return &org_model.Org{
|
||||
converted := &org_model.Org{
|
||||
ObjectRoot: org.ObjectRoot,
|
||||
Domain: org.Domain,
|
||||
Name: org.Name,
|
||||
State: org_model.OrgState(org.State),
|
||||
Domains: OrgDomainsToModel(org.Domains),
|
||||
Members: OrgMembersToModel(org.Members),
|
||||
}
|
||||
if org.OrgIamPolicy != nil {
|
||||
converted.OrgIamPolicy = OrgIamPolicyToModel(org.OrgIamPolicy)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func OrgFromEvents(org *Org, events ...*es_models.Event) (*Org, error) {
|
||||
@@ -101,6 +110,20 @@ func (o *Org) AppendEvent(event *es_models.Event) error {
|
||||
return err
|
||||
}
|
||||
o.removeMember(member.UserID)
|
||||
case OrgDomainAdded:
|
||||
o.appendAddDomainEvent(event)
|
||||
case OrgDomainVerified:
|
||||
o.appendVerifyDomainEvent(event)
|
||||
case OrgDomainPrimarySet:
|
||||
o.appendPrimaryDomainEvent(event)
|
||||
case OrgDomainRemoved:
|
||||
o.appendRemoveDomainEvent(event)
|
||||
case OrgIamPolicyAdded:
|
||||
o.appendAddOrgIamPolicyEvent(event)
|
||||
case OrgIamPolicyChanged:
|
||||
o.appendChangeOrgIamPolicyEvent(event)
|
||||
case OrgIamPolicyRemoved:
|
||||
o.appendRemoveOrgIamPolicyEvent()
|
||||
}
|
||||
|
||||
o.ObjectRoot.AppendEvent(event)
|
||||
@@ -151,9 +174,6 @@ func (o *Org) Changes(changed *Org) map[string]interface{} {
|
||||
if changed.Name != "" && changed.Name != o.Name {
|
||||
changes["name"] = changed.Name
|
||||
}
|
||||
if changed.Domain != "" && changed.Domain != o.Domain {
|
||||
changes["domain"] = changed.Domain
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
type OrgIamPolicy struct {
|
||||
models.ObjectRoot
|
||||
|
||||
Description string `json:"description,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
UserLoginMustBeDomain bool `json:"userLoginMustBeDomain"`
|
||||
}
|
||||
|
||||
func OrgIamPolicyToModel(policy *OrgIamPolicy) *org_model.OrgIamPolicy {
|
||||
return &org_model.OrgIamPolicy{
|
||||
ObjectRoot: policy.ObjectRoot,
|
||||
State: org_model.PolicyState(policy.State),
|
||||
UserLoginMustBeDomain: policy.UserLoginMustBeDomain,
|
||||
}
|
||||
}
|
||||
|
||||
func OrgIamPolicyFromModel(policy *org_model.OrgIamPolicy) *OrgIamPolicy {
|
||||
return &OrgIamPolicy{
|
||||
ObjectRoot: policy.ObjectRoot,
|
||||
State: int32(policy.State),
|
||||
UserLoginMustBeDomain: policy.UserLoginMustBeDomain,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Org) appendAddOrgIamPolicyEvent(event *es_models.Event) error {
|
||||
o.OrgIamPolicy = new(OrgIamPolicy)
|
||||
err := o.OrgIamPolicy.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.OrgIamPolicy.ObjectRoot.CreationDate = event.CreationDate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Org) appendChangeOrgIamPolicyEvent(event *es_models.Event) error {
|
||||
return o.OrgIamPolicy.SetData(event)
|
||||
}
|
||||
|
||||
func (o *Org) appendRemoveOrgIamPolicyEvent() {
|
||||
o.OrgIamPolicy = nil
|
||||
}
|
||||
|
||||
func (p *OrgIamPolicy) Changes(changed *OrgIamPolicy) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 2)
|
||||
|
||||
if changed.Description != p.Description {
|
||||
changes["description"] = changed.Description
|
||||
}
|
||||
if changed.UserLoginMustBeDomain != p.UserLoginMustBeDomain {
|
||||
changes["userLoginMustBeDomain"] = changed.UserLoginMustBeDomain
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
func (p *OrgIamPolicy) SetData(event *es_models.Event) error {
|
||||
err := json.Unmarshal(event.Data, p)
|
||||
if err != nil {
|
||||
return errors.ThrowInternal(err, "EVENT-7JS9d", "unable to unmarshal data")
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -74,10 +74,10 @@ func TestAppendEvent(t *testing.T) {
|
||||
{
|
||||
name: "append change event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgChanged, Data: []byte(`{"domain": "OrgDomain"}`)},
|
||||
org: &Org{Name: "OrgName", Domain: "asdf"},
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgChanged, Data: []byte(`{"name": "OrgName}`)},
|
||||
org: &Org{Name: "OrgNameChanged"},
|
||||
},
|
||||
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE), Name: "OrgName", Domain: "OrgDomain"},
|
||||
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE), Name: "OrgNameChanged"},
|
||||
},
|
||||
{
|
||||
name: "append deactivate event",
|
||||
@@ -93,6 +93,13 @@ func TestAppendEvent(t *testing.T) {
|
||||
},
|
||||
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE)},
|
||||
},
|
||||
{
|
||||
name: "append added domain event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgDomainAdded},
|
||||
},
|
||||
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -138,16 +145,6 @@ func TestChanges(t *testing.T) {
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org domain changes",
|
||||
args: args{
|
||||
existing: &Org{Name: "Name", Domain: "old domain"},
|
||||
new: &Org{Name: "Name", Domain: "new domain"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
args: args{
|
@@ -7,11 +7,15 @@ const (
|
||||
OrgDomainAggregate models.AggregateType = "org.domain"
|
||||
OrgNameAggregate models.AggregateType = "org.name"
|
||||
|
||||
OrgAdded models.EventType = "org.added"
|
||||
OrgChanged models.EventType = "org.changed"
|
||||
OrgDeactivated models.EventType = "org.deactivated"
|
||||
OrgReactivated models.EventType = "org.reactivated"
|
||||
OrgRemoved models.EventType = "org.removed"
|
||||
OrgAdded models.EventType = "org.added"
|
||||
OrgChanged models.EventType = "org.changed"
|
||||
OrgDeactivated models.EventType = "org.deactivated"
|
||||
OrgReactivated models.EventType = "org.reactivated"
|
||||
OrgRemoved models.EventType = "org.removed"
|
||||
OrgDomainAdded models.EventType = "org.domain.added"
|
||||
OrgDomainVerified models.EventType = "org.domain.verified"
|
||||
OrgDomainRemoved models.EventType = "org.domain.removed"
|
||||
OrgDomainPrimarySet models.EventType = "org.domain.primary.set"
|
||||
|
||||
OrgNameReserved models.EventType = "org.name.reserved"
|
||||
OrgNameReleased models.EventType = "org.name.released"
|
||||
@@ -22,4 +26,8 @@ const (
|
||||
OrgMemberAdded models.EventType = "org.member.added"
|
||||
OrgMemberChanged models.EventType = "org.member.changed"
|
||||
OrgMemberRemoved models.EventType = "org.member.removed"
|
||||
|
||||
OrgIamPolicyAdded models.EventType = "org.iam.policy.added"
|
||||
OrgIamPolicyChanged models.EventType = "org.iam.policy.changed"
|
||||
OrgIamPolicyRemoved models.EventType = "org.iam.policy.removed"
|
||||
)
|
||||
|
Reference in New Issue
Block a user