mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +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:
@@ -111,7 +111,7 @@ func (repo *AuthRequestRepo) CheckUsername(ctx context.Context, id, username str
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := repo.View.UserByUsername(username)
|
||||
user, err := repo.View.UserByLoginName(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
auth_view "github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/org/repository/view"
|
||||
"github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
)
|
||||
|
||||
type OrgRepository struct {
|
||||
@@ -24,6 +24,6 @@ func (repo *OrgRepository) SearchOrgs(ctx context.Context, request *org_model.Or
|
||||
Offset: request.Offset,
|
||||
Limit: request.Limit,
|
||||
TotalResult: uint64(count),
|
||||
Result: view.OrgsToModel(members),
|
||||
Result: model.OrgsToModel(members),
|
||||
}, nil
|
||||
}
|
||||
|
@@ -34,11 +34,15 @@ func (repo *UserRepo) Register(ctx context.Context, registerUser *model.User, or
|
||||
if resourceOwner != "" {
|
||||
policyResourceOwner = resourceOwner
|
||||
}
|
||||
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
|
||||
pwPolicy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, aggregates, err := repo.UserEvents.PrepareRegisterUser(ctx, registerUser, policy, resourceOwner)
|
||||
orgPolicy, err := repo.OrgEvents.GetOrgIamPolicy(ctx, policyResourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, aggregates, err := repo.UserEvents.PrepareRegisterUser(ctx, registerUser, pwPolicy, orgPolicy, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
global_model "github.com/caos/zitadel/internal/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
org_view "github.com/caos/zitadel/internal/org/repository/view"
|
||||
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
)
|
||||
@@ -123,7 +123,7 @@ func grantRespToOrgResp(grants *grant_model.UserGrantSearchResponse) *grant_mode
|
||||
return resp
|
||||
}
|
||||
|
||||
func orgRespToOrgResp(orgs []*org_view.OrgView, count int) *grant_model.ProjectOrgSearchResponse {
|
||||
func orgRespToOrgResp(orgs []*org_view_model.OrgView, count int) *grant_model.ProjectOrgSearchResponse {
|
||||
resp := &grant_model.ProjectOrgSearchResponse{
|
||||
TotalResult: uint64(count),
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ type EventstoreRepos struct {
|
||||
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults) []spooler.Handler {
|
||||
return []spooler.Handler{
|
||||
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}},
|
||||
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, orgEvents: repos.OrgEvents},
|
||||
&UserSession{handler: handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount}, userEvents: repos.UserEvents},
|
||||
&Token{handler: handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount}},
|
||||
&Key{handler: handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount}},
|
||||
|
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
"github.com/caos/zitadel/internal/org/repository/view"
|
||||
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ func (o *Org) EventQuery() (*es_models.SearchQuery, error) {
|
||||
}
|
||||
|
||||
func (o *Org) Process(event *es_models.Event) error {
|
||||
org := new(view.OrgView)
|
||||
org := new(org_model.OrgView)
|
||||
|
||||
switch event.Type {
|
||||
case model.OrgAdded:
|
||||
|
@@ -1,6 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
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"
|
||||
"time"
|
||||
|
||||
@@ -9,13 +14,13 @@ import (
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
handler
|
||||
eventstore eventstore.Eventstore
|
||||
orgEvents *org_events.OrgEventstore
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -33,15 +38,29 @@ func (p *User) EventQuery() (*models.SearchQuery, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eventsourcing.UserQuery(sequence), nil
|
||||
return es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate).
|
||||
LatestSequenceFilter(sequence), nil
|
||||
}
|
||||
|
||||
func (p *User) Process(event *models.Event) (err error) {
|
||||
func (u *User) Process(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 (p *User) ProcessUser(event *models.Event) (err error) {
|
||||
user := new(view_model.UserView)
|
||||
switch event.Type {
|
||||
case es_model.UserAdded,
|
||||
es_model.UserRegistered:
|
||||
user.AppendEvent(event)
|
||||
p.fillLoginNames(user)
|
||||
case es_model.UserProfileChanged,
|
||||
es_model.UserEmailChanged,
|
||||
es_model.UserEmailVerified,
|
||||
@@ -70,7 +89,72 @@ func (p *User) Process(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.PutUser(user)
|
||||
return p.view.PutUser(user, user.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.LoginNames = getLoginNames(policy, user.UserName, org.Domains)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoginNames(policy *org_model.OrgIamPolicy, userName string, domains []*org_model.OrgDomain) []string {
|
||||
loginNames := make([]string, 0)
|
||||
if !policy.UserLoginMustBeDomain {
|
||||
return []string{userName}
|
||||
}
|
||||
for _, d := range domains {
|
||||
if d.Verified {
|
||||
loginNames = append(loginNames, userName+"@"+d.Domain)
|
||||
}
|
||||
}
|
||||
return loginNames
|
||||
}
|
||||
|
||||
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)
|
||||
default:
|
||||
return u.view.ProcessedUserSequence(event.Sequence)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
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.LoginNames = getLoginNames(policy, user.UserName, org.Domains)
|
||||
err := u.view.PutUser(user, event.Sequence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *User) OnError(event *models.Event, err error) error {
|
||||
|
@@ -189,7 +189,6 @@ func (u *UserGrant) processIamMember(event *models.Event, rolePrefix string, suf
|
||||
ID: u.iamProjectID + member.UserID,
|
||||
ResourceOwner: u.iamID,
|
||||
OrgName: u.iamID,
|
||||
OrgDomain: u.iamID,
|
||||
ProjectID: u.iamProjectID,
|
||||
UserID: member.UserID,
|
||||
RoleKeys: member.Roles,
|
||||
@@ -334,7 +333,6 @@ func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *pr
|
||||
}
|
||||
|
||||
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
|
||||
grant.OrgDomain = org.Domain
|
||||
grant.OrgName = org.Name
|
||||
}
|
||||
|
||||
|
@@ -117,7 +117,7 @@ func Start(conf Config, authZ auth.Config, systemDefaults sd.SystemDefaults, aut
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es})
|
||||
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es}, systemDefaults)
|
||||
|
||||
repos := handler.EventstoreRepos{UserEvents: user, ProjectEvents: project, OrgEvents: org, IamEvents: iam}
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, repos, systemDefaults)
|
||||
|
@@ -3,6 +3,7 @@ package view
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/org/model"
|
||||
org_view "github.com/caos/zitadel/internal/org/repository/view"
|
||||
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/view"
|
||||
)
|
||||
|
||||
@@ -10,15 +11,15 @@ const (
|
||||
orgTable = "auth.orgs"
|
||||
)
|
||||
|
||||
func (v *View) OrgByID(orgID string) (*org_view.OrgView, error) {
|
||||
func (v *View) OrgByID(orgID string) (*org_model.OrgView, error) {
|
||||
return org_view.OrgByID(v.Db, orgTable, orgID)
|
||||
}
|
||||
|
||||
func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_view.OrgView, int, error) {
|
||||
func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, int, error) {
|
||||
return org_view.SearchOrgs(v.Db, orgTable, req)
|
||||
}
|
||||
|
||||
func (v *View) PutOrg(org *org_view.OrgView) error {
|
||||
func (v *View) PutOrg(org *org_model.OrgView) error {
|
||||
err := org_view.PutOrg(v.Db, orgTable, org)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -19,6 +19,13 @@ func (v *View) UserByUsername(userName string) (*model.UserView, error) {
|
||||
return view.UserByUserName(v.Db, userTable, userName)
|
||||
}
|
||||
|
||||
func (v *View) UserByLoginName(loginName string) (*model.UserView, error) {
|
||||
return view.UserByLoginName(v.Db, userTable, loginName)
|
||||
}
|
||||
|
||||
func (v *View) UsersByOrgID(orgID string) ([]*model.UserView, error) {
|
||||
return view.UsersByOrgID(v.Db, userTable, orgID)
|
||||
}
|
||||
func (v *View) SearchUsers(request *usr_model.UserSearchRequest) ([]*model.UserView, int, error) {
|
||||
return view.SearchUsers(v.Db, userTable, request)
|
||||
}
|
||||
@@ -35,12 +42,12 @@ func (v *View) UserMfas(userID string) ([]*usr_model.MultiFactor, error) {
|
||||
return view.UserMfas(v.Db, userTable, userID)
|
||||
}
|
||||
|
||||
func (v *View) PutUser(user *model.UserView) error {
|
||||
func (v *View) PutUser(user *model.UserView, sequence uint64) error {
|
||||
err := view.PutUser(v.Db, userTable, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedUserSequence(user.Sequence)
|
||||
return v.ProcessedUserSequence(sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteUser(userID string, eventSequence uint64) error {
|
||||
|
Reference in New Issue
Block a user