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:
Fabi 2020-06-16 11:40:18 +02:00 committed by GitHub
parent 64b14b4e19
commit 7a6ca24625
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 12578 additions and 6025 deletions

View File

@ -32,6 +32,9 @@ InternalAuthZ:
- "project.grant.member.read"
- "project.grant.member.write"
- "project.grant.member.delete"
- "iam.policy.read"
- "iam.policy.write"
- "iam.policy.delete"
- Role: 'ORG_OWNER'
Permissions:
- "org.read"

View File

@ -48,4 +48,7 @@ export CAOS_OIDC_DEV=true
export ZITADEL_COOKIE_DOMAIN=localhost
#Console
export ZITADEL_CONSOLE_ENV_DIR=../../console/src/assets/
export ZITADEL_CONSOLE_ENV_DIR=../../console/src/assets/
#Org
export ZITADEL_DEFAULT_DOMAIN=zitadel.ch

View File

@ -19,6 +19,7 @@ Mgmt:
- x-zitadel-
Repository:
SearchLimit: 100
Domain: $ZITADEL_DEFAULT_DOMAIN
Eventstore:
ServiceName: 'ManagementAPI'
Repository:
@ -165,6 +166,7 @@ Admin:
- x-zitadel-
Repository:
SearchLimit: 100
Domain: $ZITADEL_DEFAULT_DOMAIN
Eventstore:
ServiceName: 'Admin'
Repository:

View File

@ -63,6 +63,9 @@ SystemDefaults:
Description: Standard lockout policy
MaxAttempts: 5
ShowLockOutFailures: true
OrgIam:
Description: Standard org policy
UserLoginMustBeDomain: true
IamID: 'IAM'
SetUp:
GlobalOrg: 'Global'
@ -71,6 +74,7 @@ SystemDefaults:
- Name: 'Global'
Domain: 'global.caos.ch'
Default: true
OrgIamPolicy: true
Users:
- FirstName: 'Global Org'
LastName: 'Administrator'
@ -84,7 +88,7 @@ SystemDefaults:
Users:
- FirstName: 'Zitadel'
LastName: 'Administrator'
UserName: 'zitadel-admin@caos.ch'
UserName: 'zitadel-admin'
Email: 'zitadel-admin@caos.ch'
Password: 'Password1!'
Owners:

View File

@ -2,6 +2,7 @@ package eventstore
import (
"context"
"github.com/caos/zitadel/internal/org/repository/view/model"
admin_model "github.com/caos/zitadel/internal/admin/model"
admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
@ -9,7 +10,6 @@ import (
"github.com/caos/zitadel/internal/eventstore/sdk"
org_model "github.com/caos/zitadel/internal/org/model"
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
org_view "github.com/caos/zitadel/internal/org/repository/view"
policy_es "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
)
@ -31,7 +31,11 @@ type OrgRepo struct {
}
func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg) (*admin_model.SetupOrg, error) {
policy, err := repo.PolicyEventstore.GetPasswordComplexityPolicy(ctx, DEFAULT_POLICY)
pwPolicy, err := repo.PolicyEventstore.GetPasswordComplexityPolicy(ctx, DEFAULT_POLICY)
if err != nil {
return nil, err
}
orgPolicy, err := repo.OrgEventstore.GetOrgIamPolicy(ctx, DEFAULT_POLICY)
if err != nil {
return nil, err
}
@ -39,8 +43,7 @@ func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg)
if err != nil {
return nil, err
}
user, userAggregates, err := repo.UserEventstore.PrepareCreateUser(ctx, setUp.User, policy, org.AggregateID)
user, userAggregates, err := repo.UserEventstore.PrepareCreateUser(ctx, setUp.User, pwPolicy, orgPolicy, org.AggregateID)
if err != nil {
return nil, err
}
@ -77,10 +80,26 @@ func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchR
Offset: query.Offset,
Limit: query.Limit,
TotalResult: uint64(count),
Result: org_view.OrgsToModel(orgs),
Result: model.OrgsToModel(orgs),
}, nil
}
func (repo *OrgRepo) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) {
return repo.OrgEventstore.IsOrgUnique(ctx, name, domain)
}
func (repo *OrgRepo) GetOrgIamPolicyByID(ctx context.Context, id string) (*org_model.OrgIamPolicy, error) {
return repo.OrgEventstore.GetOrgIamPolicy(ctx, id)
}
func (repo *OrgRepo) CreateOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error) {
return repo.OrgEventstore.AddOrgIamPolicy(ctx, policy)
}
func (repo *OrgRepo) ChangeOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error) {
return repo.OrgEventstore.ChangeOrgIamPolicy(ctx, policy)
}
func (repo *OrgRepo) RemoveOrgIamPolicy(ctx context.Context, id string) error {
return repo.OrgEventstore.RemoveOrgIamPolicy(ctx, id)
}

View File

@ -3,6 +3,7 @@ package eventstore
import (
"context"
"github.com/caos/zitadel/internal/api/auth"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_model "github.com/caos/zitadel/internal/user/model"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@ -11,6 +12,7 @@ import (
type UserRepo struct {
UserEvents *usr_event.UserEventstore
PolicyEvents *policy_event.PolicyEventstore
OrgEvents *org_event.OrgEventstore
}
func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_model.User, err error) {
@ -18,11 +20,15 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_mod
}
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
pwPolicy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, policy)
orgPolicy, err := repo.OrgEvents.GetOrgIamPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, pwPolicy, orgPolicy)
}
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
@ -30,9 +36,13 @@ func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, re
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
pwPolicy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, policy, resourceOwner)
orgPolicy, err := repo.OrgEvents.GetOrgIamPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, pwPolicy, orgPolicy, resourceOwner)
}

View File

@ -2,13 +2,13 @@ package handler
import (
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"time"
"github.com/caos/logging"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/org/repository/eventsourcing"
"github.com/caos/zitadel/internal/org/repository/view"
)
type Org struct {
@ -34,7 +34,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:

View File

@ -24,6 +24,7 @@ type Config struct {
Eventstore es_int.Config
View types.SQL
Spooler spooler.SpoolerConfig
Domain string
}
type EsRepository struct {
@ -45,7 +46,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
return nil, err
}
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es})
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es, IAMDomain: conf.Domain}, systemDefaults)
project, err := es_proj.StartProject(es_proj.ProjectConfig{
Eventstore: es,
@ -80,7 +81,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
eventstoreRepos := setup.EventstoreRepos{OrgEvents: org, UserEvents: user, ProjectEvents: project, IamEvents: iam, PolicyEvents: policy}
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup")
logging.Log("SERVE-djs3R").OnError(err).Panic("failed to execute setup")
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient)

View File

@ -167,8 +167,23 @@ func (setUp *initializer) orgs(ctx context.Context, orgs []types.Org) error {
}
setUp.createdOrgs[iamOrg.Name] = org
var policy *org_model.OrgIamPolicy
if iamOrg.OrgIamPolicy {
policy, err = setUp.iamorgpolicy(ctx, org)
if err != nil {
logging.LogWithFields("SETUP-IlLif", "Org Iam Policy", iamOrg.Name).WithError(err).Error("unable to create iam org policy")
return err
}
} else {
policy, err = setUp.repos.OrgEvents.GetOrgIamPolicy(ctx, DEFAULT_POLICY)
if err != nil {
logging.LogWithFields("SETUP-IS8wS", "Org Iam Policy", iamOrg.Name).WithError(err).Error("unable to get default iam org policy")
return err
}
}
ctx = setSetUpContextData(ctx, org.AggregateID)
err = setUp.users(ctx, iamOrg.Users)
err = setUp.users(ctx, iamOrg.Users, policy)
if err != nil {
logging.LogWithFields("SETUP-8zfwz", "Org", iamOrg.Name).WithError(err).Error("unable to set up org users")
return err
@ -193,12 +208,21 @@ func (setUp *initializer) orgs(ctx context.Context, orgs []types.Org) error {
func (setUp *initializer) org(ctx context.Context, org types.Org) (*org_model.Org, error) {
ctx = setSetUpContextData(ctx, "")
createOrg := &org_model.Org{
Name: org.Name,
Domain: org.Domain,
Name: org.Name,
Domains: []*org_model.OrgDomain{&org_model.OrgDomain{Domain: org.Domain}},
}
return setUp.repos.OrgEvents.CreateOrg(ctx, createOrg)
}
func (setUp *initializer) iamorgpolicy(ctx context.Context, org *org_model.Org) (*org_model.OrgIamPolicy, error) {
ctx = setSetUpContextData(ctx, org.AggregateID)
policy := &org_model.OrgIamPolicy{
ObjectRoot: models.ObjectRoot{AggregateID: org.AggregateID},
UserLoginMustBeDomain: false,
}
return setUp.repos.OrgEvents.AddOrgIamPolicy(ctx, policy)
}
func (setUp *initializer) iamOwners(ctx context.Context, owners []string) error {
logging.Log("SETUP-dtxfj").Info("setting iam owners")
for _, iamOwner := range owners {
@ -249,9 +273,9 @@ func (setUp *initializer) setIamProject(ctx context.Context) error {
return nil
}
func (setUp *initializer) users(ctx context.Context, users []types.User) error {
func (setUp *initializer) users(ctx context.Context, users []types.User, orgPolicy *org_model.OrgIamPolicy) error {
for _, user := range users {
created, err := setUp.user(ctx, user)
created, err := setUp.user(ctx, user, orgPolicy)
if err != nil {
logging.LogWithFields("SETUP-9soer", "Email", user.Email).WithError(err).Error("unable to create iam user")
return err
@ -261,7 +285,7 @@ func (setUp *initializer) users(ctx context.Context, users []types.User) error {
return nil
}
func (setUp *initializer) user(ctx context.Context, user types.User) (*usr_model.User, error) {
func (setUp *initializer) user(ctx context.Context, user types.User, orgPolicy *org_model.OrgIamPolicy) (*usr_model.User, error) {
createUser := &usr_model.User{
Profile: &usr_model.Profile{
UserName: user.UserName,
@ -276,7 +300,7 @@ func (setUp *initializer) user(ctx context.Context, user types.User) (*usr_model
SecretString: user.Password,
},
}
return setUp.repos.UserEvents.CreateUser(ctx, createUser, setUp.pwComplexityPolicy)
return setUp.repos.UserEvents.CreateUser(ctx, createUser, setUp.pwComplexityPolicy, orgPolicy)
}
func (setUp *initializer) orgOwners(ctx context.Context, org *org_model.Org, owners []string) error {

View File

@ -3,6 +3,7 @@ package view
import (
org_model "github.com/caos/zitadel/internal/org/model"
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view"
)
@ -10,15 +11,15 @@ const (
orgTable = "admin_api.orgs"
)
func (v *View) OrgByID(orgID string) (*org_view.OrgView, error) {
func (v *View) OrgByID(orgID string) (*model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*org_view.OrgView, int, error) {
func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*model.OrgView, int, error) {
return org_view.SearchOrgs(v.Db, orgTable, query)
}
func (v *View) PutOrg(org *org_view.OrgView) error {
func (v *View) PutOrg(org *model.OrgView) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err

View File

@ -12,4 +12,9 @@ type OrgRepository interface {
IsOrgUnique(ctx context.Context, name, domain string) (bool, error)
OrgByID(ctx context.Context, id string) (*org_model.Org, error)
SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error)
GetOrgIamPolicyByID(ctx context.Context, id string) (*org_model.OrgIamPolicy, error)
CreateOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error)
ChangeOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error)
RemoveOrgIamPolicy(ctx context.Context, id string) error
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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}},

View File

@ -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:

View File

@ -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 {

View File

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

View File

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

View File

@ -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

View File

@ -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 {

View File

@ -108,7 +108,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,

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/zitadel/internal/notification/providers/email"
"github.com/caos/zitadel/internal/notification/providers/twilio"
"github.com/caos/zitadel/internal/notification/templates"
org_model "github.com/caos/zitadel/internal/org/model"
pol "github.com/caos/zitadel/internal/policy"
)
@ -50,6 +51,7 @@ type DefaultPolicies struct {
Age pol.PasswordAgePolicyDefault
Complexity pol.PasswordComplexityPolicyDefault
Lockout pol.PasswordLockoutPolicyDefault
OrgIam org_model.OrgIamPolicy
}
type Notifications struct {

View File

@ -16,11 +16,12 @@ type User struct {
}
type Org struct {
Name string
Domain string
Users []User
Owners []string
Projects []Project
Name string
Domain string
OrgIamPolicy bool
Users []User
Owners []string
Projects []Project
}
type Project struct {

View File

@ -7,6 +7,8 @@ Login:
Title: Anmeldung
Description: Gib deine Benutzerdaten ein.
Username: Benutzername
Loginname: Loginname
LoginnamePlaceHolder: username@domain
UserSelection:
Title: Account auswählen

View File

@ -2,6 +2,8 @@ Login:
Title: Login
Description: Enter your logindata.
Username: Username
Loginname: Loginname
LoginnamePlaceHolder: username@domain
UserSelection:
Title: Select account

View File

@ -1,3 +1,4 @@
{{template "main-top" .}}
<h1>{{t "Login.Title"}}</h1>
@ -9,8 +10,8 @@
<div class="fields">
<div class="field">
<label class="label" for="username">{{t "Login.Username"}}</label>
<input class="input" type="text" id="username" name="username" value="{{ .UserName }}" autocomplete="username" autofocus required>
<label class="label" for="username">{{t "Login.Loginname"}}</label>
<input class="input" type="text" id="username" name="username" placeholder="{{t "Login.LoginnamePlaceHolder"}}" value="{{ .UserName }}" autocomplete="username" autofocus required>
</div>
</div>

View File

@ -2,16 +2,15 @@ package eventstore
import (
"context"
"strings"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/model"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"strings"
"github.com/caos/zitadel/internal/errors"
mgmt_view "github.com/caos/zitadel/internal/management/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"
)
type OrgRepository struct {
@ -26,12 +25,12 @@ func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.O
return repo.OrgEventstore.OrgByID(ctx, org)
}
func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error) {
org, err := repo.View.OrgByDomain(domain)
func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.Org, error) {
verifiedDomain, err := repo.View.VerifiedOrgDomain(domain)
if err != nil {
return nil, err
}
return view.OrgToModel(org), nil
return repo.OrgByID(ctx, verifiedDomain.OrgID)
}
func (repo *OrgRepository) UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error) {
@ -46,6 +45,31 @@ func (repo *OrgRepository) ReactivateOrg(ctx context.Context, id string) (*org_m
return repo.OrgEventstore.ReactivateOrg(ctx, id)
}
func (repo *OrgRepository) SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
request.Queries = append(request.Queries, &org_model.OrgDomainSearchQuery{Key: org_model.ORGDOMAINSEARCHKEY_ORG_ID, Method: global_model.SEARCHMETHOD_EQUALS, Value: auth.GetCtxData(ctx).OrgID})
domains, count, err := repo.View.SearchOrgDomains(request)
if err != nil {
return nil, err
}
return &org_model.OrgDomainSearchResponse{
Offset: request.Offset,
Limit: request.Limit,
TotalResult: uint64(count),
Result: model.OrgDomainsToModel(domains),
}, nil
}
func (repo *OrgRepository) AddMyOrgDomain(ctx context.Context, domain *org_model.OrgDomain) (*org_model.OrgDomain, error) {
domain.AggregateID = auth.GetCtxData(ctx).OrgID
return repo.OrgEventstore.AddOrgDomain(ctx, domain)
}
func (repo *OrgRepository) RemoveMyOrgDomain(ctx context.Context, domain string) error {
d := org_model.NewOrgDomain(auth.GetCtxData(ctx).OrgID, domain)
return repo.OrgEventstore.RemoveOrgDomain(ctx, d)
}
func (repo *OrgRepository) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error) {
changes, err := repo.OrgEventstore.OrgChanges(ctx, id, lastSequence, limit)
if err != nil {
@ -76,7 +100,7 @@ func (repo *OrgRepository) RemoveMyOrgMember(ctx context.Context, userID string)
func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) {
request.EnsureLimit(repo.SearchLimit)
request.Queries[len(request.Queries)-1] = &org_model.OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_ORG_ID, Method: model.SEARCHMETHOD_EQUALS, Value: auth.GetCtxData(ctx).OrgID}
request.Queries[len(request.Queries)-1] = &org_model.OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_ORG_ID, Method: global_model.SEARCHMETHOD_EQUALS, Value: auth.GetCtxData(ctx).OrgID}
members, count, err := repo.View.SearchOrgMembers(request)
if err != nil {
return nil, err
@ -85,7 +109,7 @@ func (repo *OrgRepository) SearchMyOrgMembers(ctx context.Context, request *org_
Offset: request.Offset,
Limit: request.Limit,
TotalResult: uint64(count),
Result: view.OrgMembersToModel(members),
Result: model.OrgMembersToModel(members),
}, nil
}

View File

@ -5,6 +5,7 @@ import (
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
usr_model "github.com/caos/zitadel/internal/user/model"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@ -15,6 +16,7 @@ type UserRepo struct {
SearchLimit uint64
UserEvents *usr_event.UserEventstore
PolicyEvents *policy_event.PolicyEventstore
OrgEvents *org_event.OrgEventstore
View *view.View
}
@ -23,11 +25,15 @@ func (repo *UserRepo) UserByID(ctx context.Context, id string) (project *usr_mod
}
func (repo *UserRepo) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
pwPolicy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, policy)
orgPolicy, err := repo.OrgEvents.GetOrgIamPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.CreateUser(ctx, user, pwPolicy, orgPolicy)
}
func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
@ -35,11 +41,15 @@ func (repo *UserRepo) RegisterUser(ctx context.Context, user *usr_model.User, re
if resourceOwner != "" {
policyResourceOwner = resourceOwner
}
policy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
pwPolicy, err := repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, policyResourceOwner)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, policy, resourceOwner)
orgPolicy, err := repo.OrgEvents.GetOrgIamPolicy(ctx, auth.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return repo.UserEvents.RegisterUser(ctx, user, pwPolicy, orgPolicy, resourceOwner)
}
func (repo *UserRepo) DeactivateUser(ctx context.Context, id string) (*usr_model.User, error) {

View File

@ -43,6 +43,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev
&UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents, orgEvents: repos.OrgEvents},
&Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}},
&OrgMember{handler: handler{view, bulkLimit, configs.cycleDuration("OrgMember"), errorCount}, userEvents: repos.UserEvents},
&OrgDomain{handler: handler{view, bulkLimit, configs.cycleDuration("OrgDomain"), errorCount}},
}
}

View File

@ -2,13 +2,13 @@ package handler
import (
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"time"
"github.com/caos/logging"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/org/repository/eventsourcing"
"github.com/caos/zitadel/internal/org/repository/view"
)
type Org struct {
@ -34,7 +34,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:

View File

@ -0,0 +1,100 @@
package handler
import (
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"time"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
)
type OrgDomain struct {
handler
}
const (
orgDomainTable = "management.org_domains"
)
func (d *OrgDomain) MinimumCycleDuration() time.Duration { return d.cycleDuration }
func (d *OrgDomain) ViewModel() string {
return orgDomainTable
}
func (d *OrgDomain) EventQuery() (*models.SearchQuery, error) {
sequence, err := d.view.GetLatestOrgDomainSequence()
if err != nil {
return nil, err
}
return es_models.NewSearchQuery().
AggregateTypeFilter(model.OrgAggregate).
LatestSequenceFilter(sequence), nil
}
func (d *OrgDomain) Process(event *models.Event) (err error) {
switch event.AggregateType {
case model.OrgAggregate:
err = d.processOrgDomain(event)
}
return err
}
func (d *OrgDomain) processOrgDomain(event *models.Event) (err error) {
domain := new(org_model.OrgDomainView)
switch event.Type {
case model.OrgDomainAdded:
domain.AppendEvent(event)
case model.OrgDomainVerified:
err = domain.SetData(event)
if err != nil {
return err
}
domain, err = d.view.OrgDomainByOrgIDAndDomain(event.AggregateID, domain.Domain)
if err != nil {
return err
}
domain.AppendEvent(event)
case model.OrgDomainPrimarySet:
err = domain.SetData(event)
if err != nil {
return err
}
domain, err = d.view.OrgDomainByOrgIDAndDomain(event.AggregateID, domain.Domain)
if err != nil {
return err
}
existingDomains, err := d.view.OrgDomainsByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, existing := range existingDomains {
existing.Primary = false
err := d.view.PutOrgDomain(existing, 0)
if err != nil {
return err
}
}
domain.AppendEvent(event)
case model.OrgDomainRemoved:
err = domain.SetData(event)
if err != nil {
return err
}
return d.view.DeleteOrgDomain(domain.Domain, event.Sequence)
default:
return d.view.ProcessedOrgDomainSequence(event.Sequence)
}
if err != nil {
return err
}
return d.view.PutOrgDomain(domain, domain.Sequence)
}
func (d *OrgDomain) OnError(event *models.Event, err error) error {
logging.LogWithFields("SPOOL-us4sj", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgdomain handler")
return spooler.HandleError(event, err, d.view.GetLatestOrgDomainFailedEvent, d.view.ProcessedOrgDomainFailedEvent, d.view.ProcessedOrgDomainSequence, d.errorCountUntilSkip)
}

View File

@ -3,13 +3,13 @@ package handler
import (
"context"
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/repository/view/model"
"time"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
view_model "github.com/caos/zitadel/internal/org/repository/view"
usr_model "github.com/caos/zitadel/internal/user/model"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@ -51,7 +51,7 @@ func (m *OrgMember) Process(event *models.Event) (err error) {
}
func (m *OrgMember) processOrgMember(event *models.Event) (err error) {
member := new(view_model.OrgMemberView)
member := new(org_model.OrgMemberView)
switch event.Type {
case model.OrgMemberAdded:
member.AppendEvent(event)
@ -106,7 +106,7 @@ func (m *OrgMember) processUser(event *models.Event) (err error) {
return nil
}
func (m *OrgMember) fillData(member *view_model.OrgMemberView) (err error) {
func (m *OrgMember) fillData(member *org_model.OrgMemberView) (err error) {
user, err := m.userEvents.UserByID(context.Background(), member.UserID)
if err != nil {
return err
@ -115,7 +115,7 @@ func (m *OrgMember) fillData(member *view_model.OrgMemberView) (err error) {
return nil
}
func (m *OrgMember) fillUserData(member *view_model.OrgMemberView, user *usr_model.User) {
func (m *OrgMember) fillUserData(member *org_model.OrgMemberView, user *usr_model.User) {
member.UserName = user.UserName
member.FirstName = user.FirstName
member.LastName = user.LastName

View File

@ -95,7 +95,6 @@ func (p *ProjectGrant) Process(event *models.Event) (err error) {
}
func (p *ProjectGrant) fillOrgData(grantedProject *view_model.ProjectGrantView, org *org_model.Org) {
grantedProject.OrgDomain = org.Domain
grantedProject.OrgName = org.Name
}

View File

@ -169,7 +169,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
}

View File

@ -22,6 +22,7 @@ import (
type Config struct {
SearchLimit uint64
Domain string
Eventstore es_int.Config
View types.SQL
Spooler spooler.SpoolerConfig
@ -80,7 +81,8 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe
if err != nil {
return nil, err
}
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es})
org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es, IAMDomain: conf.Domain}, systemDefaults)
iam, err := es_iam.StartIam(es_iam.IamConfig{
Eventstore: es,
Cache: conf.Eventstore.Cache,
@ -95,7 +97,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe
spooler: spool,
OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles},
ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, view},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, policy, org, view},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view},
PolicyRepo: eventstore.PolicyRepo{policy},
IamRepository: eventstore.IamRepository{iam},

View File

@ -2,6 +2,7 @@ package view
import (
org_view "github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view"
)
@ -9,15 +10,11 @@ const (
orgTable = "management.orgs"
)
func (v *View) OrgByID(orgID string) (*org_view.OrgView, error) {
func (v *View) OrgByID(orgID string) (*model.OrgView, error) {
return org_view.OrgByID(v.Db, orgTable, orgID)
}
func (v *View) OrgByDomain(domain string) (*org_view.OrgView, error) {
return org_view.GetGlobalOrgByDomain(v.Db, orgTable, domain)
}
func (v *View) PutOrg(org *org_view.OrgView) error {
func (v *View) PutOrg(org *model.OrgView) error {
err := org_view.PutOrg(v.Db, orgTable, org)
if err != nil {
return err

View File

@ -0,0 +1,63 @@
package view
import (
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
global_view "github.com/caos/zitadel/internal/view"
)
const (
orgDomainTable = "management.org_domains"
)
func (v *View) OrgDomainByOrgIDAndDomain(orgID, domain string) (*model.OrgDomainView, error) {
return view.OrgDomainByOrgIDAndDomain(v.Db, orgDomainTable, orgID, domain)
}
func (v *View) OrgDomainsByOrgID(domain string) ([]*model.OrgDomainView, error) {
return view.OrgDomainsByOrgID(v.Db, orgDomainTable, domain)
}
func (v *View) VerifiedOrgDomain(domain string) (*model.OrgDomainView, error) {
return view.VerifiedOrgDomain(v.Db, orgDomainTable, domain)
}
func (v *View) SearchOrgDomains(request *org_model.OrgDomainSearchRequest) ([]*model.OrgDomainView, int, error) {
return view.SearchOrgDomains(v.Db, orgDomainTable, request)
}
func (v *View) PutOrgDomain(org *model.OrgDomainView, sequence uint64) error {
err := view.PutOrgDomain(v.Db, orgDomainTable, org)
if err != nil {
return err
}
if sequence != 0 {
return v.ProcessedOrgDomainSequence(sequence)
}
return nil
}
func (v *View) DeleteOrgDomain(domain string, eventSequence uint64) error {
err := view.DeleteOrgDomain(v.Db, orgDomainTable, domain)
if err != nil {
return nil
}
return v.ProcessedOrgDomainSequence(eventSequence)
}
func (v *View) GetLatestOrgDomainSequence() (uint64, error) {
return v.latestSequence(orgDomainTable)
}
func (v *View) ProcessedOrgDomainSequence(eventSequence uint64) error {
return v.saveCurrentSequence(orgDomainTable, eventSequence)
}
func (v *View) GetLatestOrgDomainFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
return v.latestFailedEvent(orgDomainTable, sequence)
}
func (v *View) ProcessedOrgDomainFailedEvent(failedEvent *global_view.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -3,6 +3,7 @@ package view
import (
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view"
"github.com/caos/zitadel/internal/org/repository/view/model"
global_view "github.com/caos/zitadel/internal/view"
)
@ -10,19 +11,19 @@ const (
orgMemberTable = "management.org_members"
)
func (v *View) OrgMemberByIDs(orgID, userID string) (*view.OrgMemberView, error) {
func (v *View) OrgMemberByIDs(orgID, userID string) (*model.OrgMemberView, error) {
return view.OrgMemberByIDs(v.Db, orgMemberTable, orgID, userID)
}
func (v *View) SearchOrgMembers(request *org_model.OrgMemberSearchRequest) ([]*view.OrgMemberView, int, error) {
func (v *View) SearchOrgMembers(request *org_model.OrgMemberSearchRequest) ([]*model.OrgMemberView, int, error) {
return view.SearchOrgMembers(v.Db, orgMemberTable, request)
}
func (v *View) OrgMembersByUserID(userID string) ([]*view.OrgMemberView, error) {
func (v *View) OrgMembersByUserID(userID string) ([]*model.OrgMemberView, error) {
return view.OrgMembersByUserID(v.Db, orgMemberTable, userID)
}
func (v *View) PutOrgMember(org *view.OrgMemberView, sequence uint64) error {
func (v *View) PutOrgMember(org *model.OrgMemberView, sequence uint64) error {
err := view.PutOrgMember(v.Db, orgMemberTable, org)
if err != nil {
return err

View File

@ -8,12 +8,16 @@ import (
type OrgRepository interface {
OrgByID(ctx context.Context, id string) (*org_model.Org, error)
OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error)
OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.Org, error)
UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error)
DeactivateOrg(ctx context.Context, id string) (*org_model.Org, error)
ReactivateOrg(ctx context.Context, id string) (*org_model.Org, error)
OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error)
SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error)
AddMyOrgDomain(ctx context.Context, domain *org_model.OrgDomain) (*org_model.OrgDomain, error)
RemoveMyOrgDomain(ctx context.Context, domain string) error
SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error)
AddMyOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error)
ChangeMyOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error)

View File

@ -13,4 +13,5 @@ const (
SEARCHMETHOD_GREATER_THAN
SEARCHMETHOD_LESS_THAN
SEARCHMETHOD_IN
SEARCHMETHOD_EQUALS_IN_ARRAY
)

View File

@ -0,0 +1,18 @@
package model
import es_models "github.com/caos/zitadel/internal/eventstore/models"
type OrgDomain struct {
es_models.ObjectRoot
Domain string
Primary bool
Verified bool
}
func NewOrgDomain(orgID, domain string) *OrgDomain {
return &OrgDomain{ObjectRoot: es_models.ObjectRoot{AggregateID: orgID}, Domain: domain}
}
func (domain *OrgDomain) IsValid() bool {
return domain.AggregateID != "" && domain.Domain != ""
}

View File

@ -0,0 +1,52 @@
package model
import (
"github.com/caos/zitadel/internal/model"
"time"
)
type OrgDomainView struct {
OrgID string
CreationDate time.Time
ChangeDate time.Time
Domain string
Primary bool
Verified bool
}
type OrgDomainSearchRequest struct {
Offset uint64
Limit uint64
SortingColumn OrgDomainSearchKey
Asc bool
Queries []*OrgDomainSearchQuery
}
type OrgDomainSearchKey int32
const (
ORGDOMAINSEARCHKEY_UNSPECIFIED OrgDomainSearchKey = iota
ORGDOMAINSEARCHKEY_DOMAIN
ORGDOMAINSEARCHKEY_ORG_ID
ORGDOMAINSEARCHKEY_VERIFIED
ORGDOMAINSEARCHKEY_PRIMARY
)
type OrgDomainSearchQuery struct {
Key OrgDomainSearchKey
Method model.SearchMethod
Value interface{}
}
type OrgDomainSearchResponse struct {
Offset uint64
Limit uint64
TotalResult uint64
Result []*OrgDomainView
}
func (r *OrgDomainSearchRequest) EnsureLimit(limit uint64) {
if r.Limit == 0 || r.Limit > limit {
r.Limit = limit
}
}

View File

@ -2,17 +2,19 @@ package model
import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
"strings"
"github.com/golang/protobuf/ptypes/timestamp"
)
type Org struct {
es_models.ObjectRoot
State OrgState
Name string
Domain string
State OrgState
Name string
Domains []*OrgDomain
Members []*OrgMember
Members []*OrgMember
OrgIamPolicy *OrgIamPolicy
}
type OrgChanges struct {
Changes []*OrgChange
@ -43,7 +45,16 @@ func (o *Org) IsActive() bool {
}
func (o *Org) IsValid() bool {
return o.Name != "" && o.Domain != ""
return o.Name != ""
}
func (o *Org) ContainsDomain(domain *OrgDomain) bool {
for _, d := range o.Domains {
if d.Domain == domain.Domain {
return true
}
}
return false
}
func (o *Org) ContainsMember(userID string) bool {
@ -54,3 +65,11 @@ func (o *Org) ContainsMember(userID string) bool {
}
return false
}
func (o *Org) nameForDomain(iamDomain string) string {
return strings.ToLower(strings.ReplaceAll(o.Name, " ", "-") + "." + iamDomain)
}
func (o *Org) AddIAMDomain(iamDomain string) {
o.Domains = append(o.Domains, &OrgDomain{Domain: o.nameForDomain(iamDomain), Verified: true, Primary: true})
}

View File

@ -0,0 +1,21 @@
package model
import (
"github.com/caos/zitadel/internal/eventstore/models"
)
type OrgIamPolicy struct {
models.ObjectRoot
Description string
State PolicyState
UserLoginMustBeDomain bool
Default bool
}
type PolicyState int32
const (
POLICYSTATE_ACTIVE PolicyState = iota
POLICYSTATE_REMOVED
)

View File

@ -42,7 +42,7 @@ const (
type OrgMemberSearchQuery struct {
Key OrgMemberSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type OrgMemberSearchResponse struct {

View File

@ -15,8 +15,7 @@ type OrgView struct {
ResourceOwner string
Sequence uint64
Name string
Domain string
Name string
}
type OrgSearchRequest struct {
@ -41,7 +40,7 @@ const (
type OrgSearchQuery struct {
Key OrgSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type OrgSearchResult struct {
@ -66,8 +65,7 @@ func OrgViewToOrg(o *OrgView) *Org {
ResourceOwner: o.ResourceOwner,
Sequence: o.Sequence,
},
Domain: o.Domain,
Name: o.Name,
State: o.State,
Name: o.Name,
State: o.State,
}
}

View File

@ -3,11 +3,11 @@ package eventsourcing
import (
"context"
"encoding/json"
"github.com/caos/zitadel/internal/config/systemdefaults"
"log"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
@ -19,17 +19,24 @@ import (
type OrgEventstore struct {
eventstore.Eventstore
idGenerator id.Generator
IAMDomain string
idGenerator id.Generator
defaultOrgIamPolicy *org_model.OrgIamPolicy
}
type OrgConfig struct {
eventstore.Eventstore
IAMDomain string
}
func StartOrg(conf OrgConfig) *OrgEventstore {
func StartOrg(conf OrgConfig, defaults systemdefaults.SystemDefaults) *OrgEventstore {
policy := defaults.DefaultPolicies.OrgIam
policy.Default = true
return &OrgEventstore{
Eventstore: conf.Eventstore,
idGenerator: id.SonyFlakeGenerator,
Eventstore: conf.Eventstore,
idGenerator: id.SonyFlakeGenerator,
IAMDomain: conf.IAMDomain,
defaultOrgIamPolicy: &policy,
}
}
@ -37,6 +44,8 @@ func (es *OrgEventstore) PrepareCreateOrg(ctx context.Context, orgModel *org_mod
if orgModel == nil || !orgModel.IsValid() {
return nil, nil, errors.ThrowInvalidArgument(nil, "EVENT-OeLSk", "org not valid")
}
orgModel.AddIAMDomain(es.IAMDomain)
id, err := es.idGenerator.Next()
if err != nil {
return nil, nil, errors.ThrowInternal(err, "EVENT-OwciI", "id gen failed")
@ -138,6 +147,53 @@ func (es *OrgEventstore) ReactivateOrg(ctx context.Context, orgID string) (*org_
return model.OrgToModel(org), nil
}
func (es *OrgEventstore) AddOrgDomain(ctx context.Context, domain *org_model.OrgDomain) (*org_model.OrgDomain, error) {
if !domain.IsValid() {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-8sFJW", "domain is invalid")
}
existing, err := es.OrgByID(ctx, org_model.NewOrg(domain.AggregateID))
if err != nil {
return nil, err
}
repoOrg := model.OrgFromModel(existing)
repoDomain := model.OrgDomainFromModel(domain)
aggregate := OrgDomainAddedAggregate(es.Eventstore.AggregateCreator(), repoOrg, repoDomain)
err = es_sdk.Push(ctx, es.PushAggregates, repoOrg.AppendEvents, aggregate)
if err != nil {
return nil, err
}
if _, d := model.GetDomain(repoOrg.Domains, domain.Domain); d != nil {
return model.OrgDomainToModel(d), nil
}
return nil, errors.ThrowInternal(nil, "EVENT-ISOP0", "Could not find org in list")
}
func (es *OrgEventstore) RemoveOrgDomain(ctx context.Context, domain *org_model.OrgDomain) error {
if domain.Domain == "" {
return errors.ThrowPreconditionFailed(nil, "EVENT-SJsK3", "Domain is required")
}
existing, err := es.OrgByID(ctx, org_model.NewOrg(domain.AggregateID))
if err != nil {
return err
}
if !existing.ContainsDomain(domain) {
return errors.ThrowPreconditionFailed(nil, "EVENT-Sjdi3", "Domain doesn't exist on project")
}
repoOrg := model.OrgFromModel(existing)
repoDomain := model.OrgDomainFromModel(domain)
orgAggregates, err := OrgDomainRemovedAggregate(ctx, es.Eventstore.AggregateCreator(), repoOrg, repoDomain)
if err != nil {
return err
}
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoOrg.AppendEvents, orgAggregates...)
if err != nil {
return err
}
return nil
}
func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error) {
query := ChangesQuery(id, lastSequence)
@ -147,7 +203,7 @@ func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence
return nil, errors.ThrowInternal(err, "EVENT-328b1", "unable to get current user")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-FpQqK", "no objects found")
return nil, errors.ThrowNotFound(nil, "EVENT-FpQqK", "no objects found")
}
result := make([]*org_model.OrgChange, 0)
@ -277,3 +333,74 @@ func (es *OrgEventstore) RemoveOrgMember(ctx context.Context, member *org_model.
orgAggregate := orgMemberRemovedAggregate(es.Eventstore.AggregateCreator(), repoMember)
return es_sdk.Push(ctx, es.PushAggregates, repoMember.AppendEvents, orgAggregate)
}
func (es *OrgEventstore) GetOrgIamPolicy(ctx context.Context, orgID string) (*org_model.OrgIamPolicy, error) {
existing, err := es.OrgByID(ctx, org_model.NewOrg(orgID))
if err != nil && !errors.IsNotFound(err) {
return nil, err
}
if existing != nil && existing.OrgIamPolicy != nil {
return existing.OrgIamPolicy, nil
}
return es.defaultOrgIamPolicy, nil
}
func (es *OrgEventstore) AddOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error) {
existing, err := es.OrgByID(ctx, org_model.NewOrg(policy.AggregateID))
if err != nil {
return nil, err
}
if existing.OrgIamPolicy != nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-7Usj3", "Policy already exists")
}
repoOrg := model.OrgFromModel(existing)
repoPolicy := model.OrgIamPolicyFromModel(policy)
orgAggregate := OrgIamPolicyAddedAggregate(es.Eventstore.AggregateCreator(), repoOrg, repoPolicy)
if err != nil {
return nil, err
}
err = es_sdk.Push(ctx, es.PushAggregates, repoOrg.AppendEvents, orgAggregate)
if err != nil {
return nil, err
}
return model.OrgIamPolicyToModel(repoOrg.OrgIamPolicy), nil
}
func (es *OrgEventstore) ChangeOrgIamPolicy(ctx context.Context, policy *org_model.OrgIamPolicy) (*org_model.OrgIamPolicy, error) {
existing, err := es.OrgByID(ctx, org_model.NewOrg(policy.AggregateID))
if err != nil {
return nil, err
}
if existing.OrgIamPolicy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-8juSd", "Policy doesnt exist")
}
repoOrg := model.OrgFromModel(existing)
repoPolicy := model.OrgIamPolicyFromModel(policy)
orgAggregate := OrgIamPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoOrg, repoPolicy)
if err != nil {
return nil, err
}
err = es_sdk.Push(ctx, es.PushAggregates, repoOrg.AppendEvents, orgAggregate)
if err != nil {
return nil, err
}
return model.OrgIamPolicyToModel(repoOrg.OrgIamPolicy), nil
}
func (es *OrgEventstore) RemoveOrgIamPolicy(ctx context.Context, orgID string) error {
existing, err := es.OrgByID(ctx, org_model.NewOrg(orgID))
if err != nil {
return err
}
if existing.OrgIamPolicy == nil {
return errors.ThrowPreconditionFailed(nil, "EVENT-z6Dse", "Policy doesnt exist")
}
repoOrg := model.OrgFromModel(existing)
orgAggregate := OrgIamPolicyRemovedAggregate(es.Eventstore.AggregateCreator(), repoOrg)
if err != nil {
return err
}
return es_sdk.Push(ctx, es.PushAggregates, repoOrg.AppendEvents, orgAggregate)
}

View File

@ -18,8 +18,7 @@ func GetMockedEventstoreComplexity(ctrl *gomock.Controller, mockEs *mock.MockEve
func GetMockChangesOrgOK(ctrl *gomock.Controller) *OrgEventstore {
org := model.Org{
Name: "MusterOrg",
Domain: "myDomain",
Name: "MusterOrg",
}
data, err := json.Marshal(org)
if err != nil {

View File

@ -1058,7 +1058,7 @@ func TestChangesOrg(t *testing.T) {
},
res: res{
changes: &org_model.OrgChanges{Changes: []*org_model.OrgChange{&org_model.OrgChange{EventType: "", Sequence: 1, Modifier: ""}}, LastSequence: 1},
org: &model.Org{Name: "MusterOrg", Domain: "myDomain"},
org: &model.Org{Name: "MusterOrg"},
},
},
{

View 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,
}
}

View File

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

View File

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

View File

@ -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{

View File

@ -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"
)

View File

@ -47,16 +47,6 @@ func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCr
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie7", "org should not be nil")
}
domainAgrregate, err := uniqueDomainAggregate(ctx, aggCreator, org.AggregateID, org.Domain)
if err != nil {
return nil, err
}
nameAggregate, err := uniqueNameAggregate(ctx, aggCreator, org.AggregateID, org.Name)
if err != nil {
return nil, err
}
agg, err := aggCreator.NewAggregate(ctx, org.AggregateID, model.OrgAggregate, model.OrgVersion, org.Sequence, es_models.OverwriteResourceOwner(org.AggregateID))
if err != nil {
return nil, err
@ -65,12 +55,44 @@ func orgCreatedAggregates(ctx context.Context, aggCreator *es_models.AggregateCr
if err != nil {
return nil, err
}
aggregates := make([]*es_models.Aggregate, 0)
aggregates, err = addDomainAggregateAndEvents(ctx, aggCreator, agg, aggregates, org)
if err != nil {
return nil, err
}
nameAggregate, err := reservedUniqueNameAggregate(ctx, aggCreator, org.AggregateID, org.Name)
if err != nil {
return nil, err
}
aggregates = append(aggregates, nameAggregate)
return append(aggregates, agg), nil
}
return []*es_models.Aggregate{
agg,
domainAgrregate,
nameAggregate,
}, nil
func addDomainAggregateAndEvents(ctx context.Context, aggCreator *es_models.AggregateCreator, orgAggregate *es_models.Aggregate, aggregates []*es_models.Aggregate, org *model.Org) ([]*es_models.Aggregate, error) {
for _, domain := range org.Domains {
orgAggregate, err := orgAggregate.AppendEvent(model.OrgDomainAdded, domain)
if err != nil {
return nil, err
}
if domain.Verified {
domainAggregate, err := reservedUniqueDomainAggregate(ctx, aggCreator, org.AggregateID, domain.Domain)
if err != nil {
return nil, err
}
aggregates = append(aggregates, domainAggregate)
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainVerified, domain)
if err != nil {
return nil, err
}
}
if domain.Primary {
orgAggregate, err = orgAggregate.AppendEvent(model.OrgDomainPrimarySet, domain)
if err != nil {
return nil, err
}
}
}
return aggregates, nil
}
func OrgUpdateAggregates(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.Org, updated *model.Org) ([]*es_models.Aggregate, error) {
@ -88,19 +110,16 @@ func OrgUpdateAggregates(ctx context.Context, aggCreator *es_models.AggregateCre
aggregates := make([]*es_models.Aggregate, 0, 3)
if name, ok := changes["name"]; ok {
nameAggregate, err := uniqueNameAggregate(ctx, aggCreator, "", name.(string))
nameAggregate, err := reservedUniqueNameAggregate(ctx, aggCreator, "", name.(string))
if err != nil {
return nil, err
}
aggregates = append(aggregates, nameAggregate)
}
if name, ok := changes["domain"]; ok {
domainAggregate, err := uniqueDomainAggregate(ctx, aggCreator, "", name.(string))
nameReleasedAggregate, err := releasedUniqueNameAggregate(ctx, aggCreator, "", existing.Name)
if err != nil {
return nil, err
}
aggregates = append(aggregates, domainAggregate)
aggregates = append(aggregates, nameReleasedAggregate)
}
orgAggregate, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
@ -151,7 +170,7 @@ func orgReactivateAggregate(aggCreator *es_models.AggregateCreator, org *model.O
}
}
func uniqueDomainAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, domain string) (*es_models.Aggregate, error) {
func reservedUniqueDomainAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, domain string) (*es_models.Aggregate, error) {
aggregate, err := aggCreator.NewAggregate(ctx, domain, model.OrgDomainAggregate, model.OrgVersion, 0)
if resourceOwner != "" {
aggregate, err = aggCreator.NewAggregate(ctx, domain, model.OrgDomainAggregate, model.OrgVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
@ -164,10 +183,26 @@ func uniqueDomainAggregate(ctx context.Context, aggCreator *es_models.AggregateC
return nil, err
}
return aggregate.SetPrecondition(OrgDomainUniqueQuery(domain), isReservedValidation(aggregate, model.OrgDomainReserved)), nil
return aggregate.SetPrecondition(OrgDomainUniqueQuery(domain), isEventValidation(aggregate, model.OrgDomainReserved)), nil
}
func uniqueNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, name string) (aggregate *es_models.Aggregate, err error) {
func releasedUniqueDomainAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, domain string) (*es_models.Aggregate, error) {
aggregate, err := aggCreator.NewAggregate(ctx, domain, model.OrgDomainAggregate, model.OrgVersion, 0)
if resourceOwner != "" {
aggregate, err = aggCreator.NewAggregate(ctx, domain, model.OrgDomainAggregate, model.OrgVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
}
if err != nil {
return nil, err
}
aggregate, err = aggregate.AppendEvent(model.OrgDomainReleased, nil)
if err != nil {
return nil, err
}
return aggregate.SetPrecondition(OrgDomainUniqueQuery(domain), isEventValidation(aggregate, model.OrgDomainReleased)), nil
}
func reservedUniqueNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, name string) (aggregate *es_models.Aggregate, err error) {
aggregate, err = aggCreator.NewAggregate(ctx, name, model.OrgNameAggregate, model.OrgVersion, 0)
if resourceOwner != "" {
aggregate, err = aggCreator.NewAggregate(ctx, name, model.OrgNameAggregate, model.OrgVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
@ -180,17 +215,103 @@ func uniqueNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCre
return nil, err
}
return aggregate.SetPrecondition(OrgNameUniqueQuery(name), isReservedValidation(aggregate, model.OrgNameReserved)), nil
return aggregate.SetPrecondition(OrgNameUniqueQuery(name), isEventValidation(aggregate, model.OrgNameReserved)), nil
}
func isReservedValidation(aggregate *es_models.Aggregate, resevedEventType es_models.EventType) func(...*es_models.Event) error {
func releasedUniqueNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, name string) (aggregate *es_models.Aggregate, err error) {
aggregate, err = aggCreator.NewAggregate(ctx, name, model.OrgNameAggregate, model.OrgVersion, 0)
if resourceOwner != "" {
aggregate, err = aggCreator.NewAggregate(ctx, name, model.OrgNameAggregate, model.OrgVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
}
if err != nil {
return nil, err
}
aggregate, err = aggregate.AppendEvent(model.OrgNameReleased, nil)
if err != nil {
return nil, err
}
return aggregate.SetPrecondition(OrgNameUniqueQuery(name), isEventValidation(aggregate, model.OrgNameReleased)), nil
}
func OrgDomainAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if domain == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-OSid3", "domain should not be nil")
}
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.OrgDomainAdded, domain)
}
}
func OrgDomainVerifiedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain) func(ctx context.Context) ([]*es_models.Aggregate, error) {
return func(ctx context.Context) ([]*es_models.Aggregate, error) {
if domain == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-DHs7s", "domain should not be nil")
}
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
aggregates := make([]*es_models.Aggregate, 0, 2)
agg, err = agg.AppendEvent(model.OrgDomainVerified, domain)
if err != nil {
return nil, err
}
domainAgregate, err := reservedUniqueDomainAggregate(ctx, aggCreator, existing.AggregateID, domain.Domain)
if err != nil {
return nil, err
}
aggregates = append(aggregates, domainAgregate)
return append(aggregates, agg), nil
}
}
func OrgDomainSetPrimaryAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if domain == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-PSw3j", "domain should not be nil")
}
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.OrgDomainPrimarySet, domain)
}
}
func OrgDomainRemovedAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *model.Org, domain *model.OrgDomain) ([]*es_models.Aggregate, error) {
if domain == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-si8dW", "domain should not be nil")
}
aggregates := make([]*es_models.Aggregate, 0, 2)
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
agg, err = agg.AppendEvent(model.OrgDomainRemoved, domain)
if err != nil {
return nil, err
}
aggregates = append(aggregates, agg)
domainAgregate, err := releasedUniqueDomainAggregate(ctx, aggCreator, existing.AggregateID, domain.Domain)
if err != nil {
return nil, err
}
return append(aggregates, domainAgregate), 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 {
aggregate.PreviousSequence = 0
return nil
}
if events[0].Type == resevedEventType {
return errors.ThrowPreconditionFailed(nil, "EVENT-eJQqe", "org already reseved")
if events[0].Type == eventType {
return errors.ThrowPreconditionFailedf(nil, "EVENT-eJQqe", "user is already %v", eventType)
}
aggregate.PreviousSequence = events[0].Sequence
return nil

View File

@ -0,0 +1,48 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
)
func OrgIamPolicyAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org, policy *model.OrgIamPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-i9sJS", "policy should not be nil")
}
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.OrgIamPolicyAdded, policy)
}
}
func OrgIamPolicyChangedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org, policy *model.OrgIamPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-9Ksie", "policy should not be nil")
}
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
changes := existing.OrgIamPolicy.Changes(policy)
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-Js6Vs", "no changes")
}
return agg.AppendEvent(model.OrgIamPolicyChanged, changes)
}
}
func OrgIamPolicyRemovedAggregate(aggCreator *es_models.AggregateCreator, existing *model.Org) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
agg, err := OrgAggregate(ctx, aggCreator, existing.AggregateID, existing.Sequence)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.OrgIamPolicyRemoved, nil)
}
}

View File

@ -0,0 +1,237 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"testing"
)
func TestOrgIamPolicyAddedAggregates(t *testing.T) {
type res struct {
eventsCount int
eventType es_models.EventType
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
policy *model.OrgIamPolicy
}
tests := []struct {
name string
args args
res res
}{
{
name: "no policy error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
{
name: "policy successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
},
policy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: true,
},
},
res: res{
eventsCount: 1,
eventType: model.OrgIamPolicyAdded,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgIamPolicyAddedAggregate(tt.args.aggCreator, tt.args.org, tt.args.policy)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && got.Events[0].Type != tt.res.eventType {
t.Errorf("OrgIamPolicyAddedAggregate() event type = %v, wanted count %v", got.Events[0].Type, tt.res.eventType)
}
if tt.res.isErr == nil && len(got.Events) != tt.res.eventsCount {
t.Errorf("OrgIamPolicyAddedAggregate() event count = %d, wanted count %d", len(got.Events), tt.res.eventsCount)
}
})
}
}
func TestOrgIamPolicyChangedAggregates(t *testing.T) {
type res struct {
eventsCount int
eventType es_models.EventType
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
policy *model.OrgIamPolicy
}
tests := []struct {
name string
args args
res res
}{
{
name: "no policy error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
{
name: "policy successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
OrgIamPolicy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: true,
},
},
policy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: false,
},
},
res: res{
eventsCount: 1,
eventType: model.OrgIamPolicyChanged,
isErr: nil,
},
},
{
name: "policy no changes",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
OrgIamPolicy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: true,
},
},
policy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: true,
},
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgIamPolicyChangedAggregate(tt.args.aggCreator, tt.args.org, tt.args.policy)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && got.Events[0].Type != tt.res.eventType {
t.Errorf("OrgIamPolicyChangedAggregate() event type = %v, wanted count %v", got.Events[0].Type, tt.res.eventType)
}
if tt.res.isErr == nil && len(got.Events) != tt.res.eventsCount {
t.Errorf("OrgIamPolicyChangedAggregate() event count = %d, wanted count %d", len(got.Events), tt.res.eventsCount)
}
})
}
}
func TestOrgIamPolicyRemovedAggregates(t *testing.T) {
type res struct {
eventsCount int
eventType es_models.EventType
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
OrgIamPolicy: &model.OrgIamPolicy{
Description: "description",
UserLoginMustBeDomain: true,
},
},
},
res: res{
eventsCount: 1,
eventType: model.OrgIamPolicyRemoved,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgIamPolicyRemovedAggregate(tt.args.aggCreator, tt.args.org)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && got.Events[0].Type != tt.res.eventType {
t.Errorf("OrgIamPolicyChangedAggregate() event type = %v, wanted count %v", got.Events[0].Type, tt.res.eventType)
}
if tt.res.isErr == nil && len(got.Events) != tt.res.eventsCount {
t.Errorf("OrgIamPolicyChangedAggregate() event count = %d, wanted count %d", len(got.Events), tt.res.eventsCount)
}
})
}
}

View File

@ -79,7 +79,7 @@ func Test_isReservedValidation(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
validate := isReservedValidation(tt.args.aggregate, tt.args.eventType)
validate := isEventValidation(tt.args.aggregate, tt.args.eventType)
err := validate(tt.args.Events...)
@ -142,7 +142,7 @@ func Test_uniqueNameAggregate(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := uniqueNameAggregate(tt.args.ctx, tt.args.aggCreator, "", tt.args.orgName)
got, err := reservedUniqueNameAggregate(tt.args.ctx, tt.args.aggCreator, "", tt.args.orgName)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got: %v", err)
}
@ -198,7 +198,7 @@ func Test_uniqueDomainAggregate(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := uniqueDomainAggregate(tt.args.ctx, tt.args.aggCreator, "", tt.args.orgDomain)
got, err := reservedUniqueDomainAggregate(tt.args.ctx, tt.args.aggCreator, "", tt.args.orgDomain)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got: %v", err)
}
@ -425,47 +425,18 @@ func TestOrgUpdateAggregates(t *testing.T) {
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.ch",
Name: "coas",
Name: "coas",
},
updated: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.ch",
Name: "caos",
Name: "caos",
},
},
res: res{
aggregateCount: 2,
isErr: nil,
},
},
{
name: "domain changed",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
existing: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.swiss",
Name: "caos",
},
updated: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.ch",
Name: "caos",
},
},
res: res{
aggregateCount: 2,
aggregateCount: 3,
isErr: nil,
},
},
@ -523,17 +494,16 @@ func TestOrgCreatedAggregates(t *testing.T) {
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.ch",
Name: "caos",
Name: "caos",
},
},
res: res{
aggregateCount: 3,
aggregateCount: 2,
isErr: nil,
},
},
{
name: "no domain error",
name: "org with domain successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
@ -543,11 +513,14 @@ func TestOrgCreatedAggregates(t *testing.T) {
Sequence: 5,
},
Name: "caos",
Domains: []*model.OrgDomain{&model.OrgDomain{
Domain: "caos.ch",
}},
},
},
res: res{
aggregateCount: 2,
isErr: errors.IsPreconditionFailed,
isErr: nil,
},
},
{
@ -560,7 +533,6 @@ func TestOrgCreatedAggregates(t *testing.T) {
AggregateID: "sdaf",
Sequence: 5,
},
Domain: "caos.ch",
},
},
res: res{
@ -584,3 +556,270 @@ func TestOrgCreatedAggregates(t *testing.T) {
})
}
}
func TestOrgDomainAddedAggregates(t *testing.T) {
type res struct {
eventCount int
eventType es_models.EventType
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
domain *model.OrgDomain
}
tests := []struct {
name string
args args
res res
}{
{
name: "no domain error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
{
name: "domain successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
},
domain: &model.OrgDomain{
Domain: "caos.ch",
},
},
res: res{
eventCount: 1,
eventType: model.OrgDomainAdded,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgDomainAddedAggregate(tt.args.aggCreator, tt.args.org, tt.args.domain)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && got.Events[0].Type != tt.res.eventType {
t.Errorf("OrgDomainAddedAggregate() event type = %v, wanted count %v", got.Events[0].Type, tt.res.eventType)
}
if tt.res.isErr == nil && len(got.Events) != tt.res.eventCount {
t.Errorf("OrgDomainAddedAggregate() event count = %v, wanted count %v", len(got.Events), tt.res.eventCount)
}
})
}
}
func TestOrgDomainVerifiedAggregates(t *testing.T) {
type res struct {
aggregateCount int
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
domain *model.OrgDomain
}
tests := []struct {
name string
args args
res res
}{
{
name: "no domain error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
{
name: "domain successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
},
domain: &model.OrgDomain{
Domain: "caos.ch",
},
},
res: res{
aggregateCount: 2,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgDomainVerifiedAggregate(tt.args.aggCreator, tt.args.org, tt.args.domain)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && len(got) != tt.res.aggregateCount {
t.Errorf("OrgDomainVerifiedAggregate() aggregate count = %d, wanted count %d", len(got), tt.res.aggregateCount)
}
})
}
}
func TestOrgDomainSetPrimaryAggregates(t *testing.T) {
type res struct {
eventsCount int
eventType es_models.EventType
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
domain *model.OrgDomain
}
tests := []struct {
name string
args args
res res
}{
{
name: "no domain error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
isErr: errors.IsPreconditionFailed,
},
},
{
name: "domain successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
},
domain: &model.OrgDomain{
Domain: "caos.ch",
},
},
res: res{
eventsCount: 1,
eventType: model.OrgDomainPrimarySet,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg := OrgDomainSetPrimaryAggregate(tt.args.aggCreator, tt.args.org, tt.args.domain)
got, err := agg(tt.args.ctx)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && got.Events[0].Type != tt.res.eventType {
t.Errorf("OrgDomainSetPrimaryAggregate() event type = %v, wanted count %v", got.Events[0].Type, tt.res.eventType)
}
if tt.res.isErr == nil && len(got.Events) != tt.res.eventsCount {
t.Errorf("OrgDomainSetPrimaryAggregate() event count = %d, wanted count %d", len(got.Events), tt.res.eventsCount)
}
})
}
}
func TestOrgDomainRemovedAggregates(t *testing.T) {
type res struct {
aggregateCount int
isErr func(error) bool
}
type args struct {
ctx context.Context
aggCreator *es_models.AggregateCreator
org *model.Org
domain *model.OrgDomain
}
tests := []struct {
name string
args args
res res
}{
{
name: "no domain error",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
},
res: res{
aggregateCount: 0,
isErr: errors.IsPreconditionFailed,
},
},
{
name: "domain successful",
args: args{
ctx: auth.NewMockContext("org", "user"),
aggCreator: es_models.NewAggregateCreator("test"),
org: &model.Org{
ObjectRoot: es_models.ObjectRoot{
AggregateID: "sdaf",
Sequence: 5,
},
},
domain: &model.OrgDomain{
Domain: "caos.ch",
},
},
res: res{
aggregateCount: 2,
isErr: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := OrgDomainRemovedAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.org, tt.args.domain)
if tt.res.isErr == nil && err != nil {
t.Errorf("no error expected got %T: %v", err, err)
}
if tt.res.isErr != nil && !tt.res.isErr(err) {
t.Errorf("wrong error got %T: %v", err, err)
}
if tt.res.isErr == nil && len(got) != tt.res.aggregateCount {
t.Errorf("OrgDomainRemovedAggregate() aggregate count = %d, wanted count %d", len(got), tt.res.aggregateCount)
}
})
}
}

View File

@ -1,4 +1,4 @@
package view
package model
import (
"encoding/json"
@ -33,7 +33,6 @@ type OrgView struct {
func OrgFromModel(org *org_model.OrgView) *OrgView {
return &OrgView{
Domain: org.Domain,
ChangeDate: org.ChangeDate,
CreationDate: org.CreationDate,
ID: org.ID,
@ -46,7 +45,6 @@ func OrgFromModel(org *org_model.OrgView) *OrgView {
func OrgToModel(org *OrgView) *org_model.OrgView {
return &org_model.OrgView{
Domain: org.Domain,
ChangeDate: org.ChangeDate,
CreationDate: org.CreationDate,
ID: org.ID,

View File

@ -0,0 +1,87 @@
package model
import (
"encoding/json"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/model"
es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"time"
)
const (
OrgDomainKeyOrgID = "org_id"
OrgDomainKeyDomain = "domain"
OrgDomainKeyVerified = "verified"
OrgDomainKeyPrimary = "primary_domain"
)
type OrgDomainView struct {
Domain string `json:"domain" gorm:"column:domain;primary_key"`
OrgID string `json:"-" gorm:"column:org_id;primary_key"`
Verified bool `json:"-" gorm:"column:verified"`
Primary bool `json:"-" gorm:"column:primary_domain"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
}
func OrgDomainViewFromModel(domain *model.OrgDomainView) *OrgDomainView {
return &OrgDomainView{
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
}
}
func OrgDomainToModel(domain *OrgDomainView) *model.OrgDomainView {
return &model.OrgDomainView{
OrgID: domain.OrgID,
Domain: domain.Domain,
Primary: domain.Primary,
Verified: domain.Verified,
CreationDate: domain.CreationDate,
ChangeDate: domain.ChangeDate,
}
}
func OrgDomainsToModel(domain []*OrgDomainView) []*model.OrgDomainView {
result := make([]*model.OrgDomainView, len(domain))
for i, r := range domain {
result[i] = OrgDomainToModel(r)
}
return result
}
func (d *OrgDomainView) AppendEvent(event *models.Event) (err error) {
d.Sequence = event.Sequence
d.ChangeDate = event.CreationDate
switch event.Type {
case es_model.OrgDomainAdded:
d.setRootData(event)
d.CreationDate = event.CreationDate
err = d.SetData(event)
case es_model.OrgDomainVerified:
d.Verified = true
case es_model.OrgDomainPrimarySet:
d.Primary = true
}
return err
}
func (r *OrgDomainView) setRootData(event *models.Event) {
r.OrgID = event.AggregateID
}
func (r *OrgDomainView) SetData(event *models.Event) error {
if err := json.Unmarshal(event.Data, r); err != nil {
logging.Log("EVEN-sj4Sf").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-lub6s", "Could not unmarshal data")
}
return nil
}

View File

@ -0,0 +1,65 @@
package model
import (
global_model "github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/view"
)
type OrgDomainSearchRequest org_model.OrgDomainSearchRequest
type OrgDomainSearchQuery org_model.OrgDomainSearchQuery
type OrgDomainSearchKey org_model.OrgDomainSearchKey
func (req OrgDomainSearchRequest) GetLimit() uint64 {
return req.Limit
}
func (req OrgDomainSearchRequest) GetOffset() uint64 {
return req.Offset
}
func (req OrgDomainSearchRequest) GetSortingColumn() view.ColumnKey {
if req.SortingColumn == org_model.ORGDOMAINSEARCHKEY_UNSPECIFIED {
return nil
}
return OrgDomainSearchKey(req.SortingColumn)
}
func (req OrgDomainSearchRequest) GetAsc() bool {
return req.Asc
}
func (req OrgDomainSearchRequest) GetQueries() []view.SearchQuery {
result := make([]view.SearchQuery, len(req.Queries))
for i, q := range req.Queries {
result[i] = OrgDomainSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
}
return result
}
func (req OrgDomainSearchQuery) GetKey() view.ColumnKey {
return OrgDomainSearchKey(req.Key)
}
func (req OrgDomainSearchQuery) GetMethod() global_model.SearchMethod {
return req.Method
}
func (req OrgDomainSearchQuery) GetValue() interface{} {
return req.Value
}
func (key OrgDomainSearchKey) ToColumnName() string {
switch org_model.OrgDomainSearchKey(key) {
case org_model.ORGDOMAINSEARCHKEY_DOMAIN:
return OrgDomainKeyDomain
case org_model.ORGDOMAINSEARCHKEY_ORG_ID:
return OrgDomainKeyOrgID
case org_model.ORGDOMAINSEARCHKEY_VERIFIED:
return OrgDomainKeyVerified
case org_model.ORGDOMAINSEARCHKEY_PRIMARY:
return OrgDomainKeyPrimary
default:
return ""
}
}

View File

@ -1,4 +1,4 @@
package view
package model
import (
"encoding/json"

View File

@ -1,4 +1,4 @@
package view
package model
import (
global_model "github.com/caos/zitadel/internal/model"

View File

@ -1,4 +1,4 @@
package view
package model
import (
global_model "github.com/caos/zitadel/internal/model"

View File

@ -0,0 +1,64 @@
package view
import (
global_model "github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view"
"github.com/jinzhu/gorm"
)
func OrgDomainByOrgIDAndDomain(db *gorm.DB, table, orgID, domain string) (*model.OrgDomainView, error) {
domainView := new(model.OrgDomainView)
orgIDQuery := &model.OrgDomainSearchQuery{Key: org_model.ORGDOMAINSEARCHKEY_ORG_ID, Value: orgID, Method: global_model.SEARCHMETHOD_EQUALS}
domainQuery := &model.OrgDomainSearchQuery{Key: org_model.ORGDOMAINSEARCHKEY_DOMAIN, Value: domain, Method: global_model.SEARCHMETHOD_EQUALS}
query := view.PrepareGetByQuery(table, orgIDQuery, domainQuery)
err := query(db, domainView)
return domainView, err
}
func VerifiedOrgDomain(db *gorm.DB, table, domain string) (*model.OrgDomainView, error) {
domainView := new(model.OrgDomainView)
domainQuery := &model.OrgDomainSearchQuery{Key: org_model.ORGDOMAINSEARCHKEY_DOMAIN, Value: domain, Method: global_model.SEARCHMETHOD_EQUALS}
verifiedQuery := &model.OrgDomainSearchQuery{Key: org_model.ORGDOMAINSEARCHKEY_VERIFIED, Value: true, Method: global_model.SEARCHMETHOD_EQUALS}
query := view.PrepareGetByQuery(table, domainQuery, verifiedQuery)
err := query(db, domainView)
return domainView, err
}
func SearchOrgDomains(db *gorm.DB, table string, req *org_model.OrgDomainSearchRequest) ([]*model.OrgDomainView, int, error) {
members := make([]*model.OrgDomainView, 0)
query := view.PrepareSearchQuery(table, model.OrgDomainSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
count, err := query(db, &members)
if err != nil {
return nil, 0, err
}
return members, count, nil
}
func OrgDomainsByOrgID(db *gorm.DB, table string, orgID string) ([]*model.OrgDomainView, error) {
domains := make([]*model.OrgDomainView, 0)
queries := []*org_model.OrgDomainSearchQuery{
{
Key: org_model.ORGDOMAINSEARCHKEY_ORG_ID,
Value: orgID,
Method: global_model.SEARCHMETHOD_EQUALS,
},
}
query := view.PrepareSearchQuery(table, model.OrgDomainSearchRequest{Queries: queries})
_, err := query(db, &domains)
if err != nil {
return nil, err
}
return domains, nil
}
func PutOrgDomain(db *gorm.DB, table string, role *model.OrgDomainView) error {
save := view.PrepareSave(table)
return save(db, role)
}
func DeleteOrgDomain(db *gorm.DB, table, domain string) error {
delete := view.PrepareDeleteByKey(table, model.OrgSearchKey(org_model.ORGDOMAINSEARCHKEY_DOMAIN), domain)
return delete(db)
}

View File

@ -3,31 +3,32 @@ package view
import (
global_model "github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view"
"github.com/jinzhu/gorm"
)
func OrgMemberByIDs(db *gorm.DB, table, orgID, userID string) (*OrgMemberView, error) {
member := new(OrgMemberView)
func OrgMemberByIDs(db *gorm.DB, table, orgID, userID string) (*model.OrgMemberView, error) {
member := new(model.OrgMemberView)
orgIDQuery := &OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_ORG_ID, Value: orgID, Method: global_model.SEARCHMETHOD_EQUALS}
userIDQuery := &OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_USER_ID, Value: userID, Method: global_model.SEARCHMETHOD_EQUALS}
orgIDQuery := &model.OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_ORG_ID, Value: orgID, Method: global_model.SEARCHMETHOD_EQUALS}
userIDQuery := &model.OrgMemberSearchQuery{Key: org_model.ORGMEMBERSEARCHKEY_USER_ID, Value: userID, Method: global_model.SEARCHMETHOD_EQUALS}
query := view.PrepareGetByQuery(table, orgIDQuery, userIDQuery)
err := query(db, member)
return member, err
}
func SearchOrgMembers(db *gorm.DB, table string, req *org_model.OrgMemberSearchRequest) ([]*OrgMemberView, int, error) {
members := make([]*OrgMemberView, 0)
query := view.PrepareSearchQuery(table, OrgMemberSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
func SearchOrgMembers(db *gorm.DB, table string, req *org_model.OrgMemberSearchRequest) ([]*model.OrgMemberView, int, error) {
members := make([]*model.OrgMemberView, 0)
query := view.PrepareSearchQuery(table, model.OrgMemberSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
count, err := query(db, &members)
if err != nil {
return nil, 0, err
}
return members, count, nil
}
func OrgMembersByUserID(db *gorm.DB, table string, userID string) ([]*OrgMemberView, error) {
members := make([]*OrgMemberView, 0)
func OrgMembersByUserID(db *gorm.DB, table string, userID string) ([]*model.OrgMemberView, error) {
members := make([]*model.OrgMemberView, 0)
queries := []*org_model.OrgMemberSearchQuery{
{
Key: org_model.ORGMEMBERSEARCHKEY_USER_ID,
@ -35,7 +36,7 @@ func OrgMembersByUserID(db *gorm.DB, table string, userID string) ([]*OrgMemberV
Method: global_model.SEARCHMETHOD_EQUALS,
},
}
query := view.PrepareSearchQuery(table, OrgMemberSearchRequest{Queries: queries})
query := view.PrepareSearchQuery(table, model.OrgMemberSearchRequest{Queries: queries})
_, err := query(db, &members)
if err != nil {
return nil, err
@ -43,7 +44,7 @@ func OrgMembersByUserID(db *gorm.DB, table string, userID string) ([]*OrgMemberV
return members, nil
}
func PutOrgMember(db *gorm.DB, table string, role *OrgMemberView) error {
func PutOrgMember(db *gorm.DB, table string, role *model.OrgMemberView) error {
save := view.PrepareSave(table)
return save(db, role)
}

View File

@ -2,20 +2,21 @@ package view
import (
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"github.com/caos/zitadel/internal/view"
"github.com/jinzhu/gorm"
)
func OrgByID(db *gorm.DB, table, orgID string) (*OrgView, error) {
org := new(OrgView)
query := view.PrepareGetByKey(table, OrgSearchKey(org_model.ORGSEARCHKEY_ORG_ID), orgID)
func OrgByID(db *gorm.DB, table, orgID string) (*model.OrgView, error) {
org := new(model.OrgView)
query := view.PrepareGetByKey(table, model.OrgSearchKey(org_model.ORGSEARCHKEY_ORG_ID), orgID)
err := query(db, org)
return org, err
}
func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*OrgView, int, error) {
orgs := make([]*OrgView, 0)
query := view.PrepareSearchQuery(table, OrgSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*model.OrgView, int, error) {
orgs := make([]*model.OrgView, 0)
query := view.PrepareSearchQuery(table, model.OrgSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
count, err := query(db, &orgs)
if err != nil {
return nil, 0, err
@ -23,19 +24,12 @@ func SearchOrgs(db *gorm.DB, table string, req *org_model.OrgSearchRequest) ([]*
return orgs, count, nil
}
func GetGlobalOrgByDomain(db *gorm.DB, table, domain string) (*OrgView, error) {
org := new(OrgView)
query := view.PrepareGetByKey(table, OrgSearchKey(org_model.ORGSEARCHKEY_ORG_DOMAIN), domain)
err := query(db, org)
return org, err
}
func PutOrg(db *gorm.DB, table string, org *OrgView) error {
func PutOrg(db *gorm.DB, table string, org *model.OrgView) error {
save := view.PrepareSave(table)
return save(db, org)
}
func DeleteOrg(db *gorm.DB, table, orgID string) error {
delete := view.PrepareDeleteByKey(table, OrgSearchKey(org_model.ORGSEARCHKEY_ORG_ID), orgID)
delete := view.PrepareDeleteByKey(table, model.OrgSearchKey(org_model.ORGSEARCHKEY_ORG_ID), orgID)
return delete(db)
}

View File

@ -13,7 +13,6 @@ func PasswordAgePolicyQuery(recourceOwner string, latestSequence uint64) *es_mod
AggregateTypeFilter(model.PasswordAgePolicyAggregate).
LatestSequenceFilter(latestSequence).
ResourceOwnerFilter(recourceOwner)
}
func PasswordAgePolicyAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, policy *PasswordAgePolicy) (*es_models.Aggregate, error) {

View File

@ -46,7 +46,7 @@ const (
type ApplicationSearchQuery struct {
Key ApplicationSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type ApplicationSearchResponse struct {

View File

@ -42,7 +42,7 @@ const (
type ProjectGrantMemberSearchQuery struct {
Key ProjectGrantMemberSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type ProjectGrantMemberSearchResponse struct {

View File

@ -41,7 +41,7 @@ const (
type ProjectMemberSearchQuery struct {
Key ProjectMemberSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type ProjectMemberSearchResponse struct {

View File

@ -38,7 +38,7 @@ const (
type ProjectRoleSearchQuery struct {
Key ProjectRoleSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type ProjectRoleSearchResponse struct {

View File

@ -29,7 +29,6 @@ type ProjectGrantView struct {
State int32 `json:"-" gorm:"column:project_state"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
OrgName string `json:"-" gorm:"column:org_name"`
OrgDomain string `json:"-" gorm:"column:org_domain"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
GrantedRoleKeys pq.StringArray `json:"-" gorm:"column:granted_role_keys"`
}

View File

@ -1,9 +1,11 @@
package model
import (
"time"
caos_errors "github.com/caos/zitadel/internal/errors"
org_model "github.com/caos/zitadel/internal/org/model"
policy_model "github.com/caos/zitadel/internal/policy/model"
"strings"
"time"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/caos/zitadel/internal/crypto"
@ -66,10 +68,17 @@ const (
GENDER_DIVERSE
)
func (u *User) SetEmailAsUsername() {
if u.Profile != nil && u.UserName == "" && u.Email != nil {
func (u *User) CheckOrgIamPolicy(policy *org_model.OrgIamPolicy) error {
if policy == nil {
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-zSH7j", "Org Iam Policy should not be nil")
}
if policy.UserLoginMustBeDomain && strings.Contains(u.UserName, "@") {
return caos_errors.ThrowPreconditionFailed(nil, "MODEL-se4sJ", "Username should not be email address")
}
if !policy.UserLoginMustBeDomain && u.Profile != nil && u.UserName == "" && u.Email != nil {
u.UserName = u.EmailAddress
}
return nil
}
func (u *User) IsValid() bool {

View File

@ -44,7 +44,7 @@ const (
type UserSessionSearchQuery struct {
Key UserSessionSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type UserSessionSearchResponse struct {

View File

@ -18,6 +18,7 @@ type UserView struct {
PasswordChanged time.Time
LastLogin time.Time
UserName string
LoginNames []string
FirstName string
LastName string
NickName string
@ -61,12 +62,13 @@ const (
USERSEARCHKEY_EMAIL
USERSEARCHKEY_STATE
USERSEARCHKEY_RESOURCEOWNER
USERSEARCHKEY_LOGIN_NAMES
)
type UserSearchQuery struct {
Key UserSearchKey
Method model.SearchMethod
Value string
Value interface{}
}
type UserSearchResponse struct {

View File

@ -8,6 +8,7 @@ import (
"github.com/caos/logging"
"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"
@ -103,8 +104,8 @@ func (es *UserEventstore) UserEventsByID(ctx context.Context, id string, sequenc
return es.FilterEvents(ctx, query)
}
func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
user.SetEmailAsUsername()
func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, pwPolicy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
err := user.CheckOrgIamPolicy(orgIamPolicy)
if !user.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "User is invalid")
}
@ -115,7 +116,7 @@ func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model
}
user.AggregateID = id
err = user.HashPasswordIfExisting(policy, es.PasswordAlg, true)
err = user.HashPasswordIfExisting(pwPolicy, es.PasswordAlg, true)
if err != nil {
return nil, nil, err
}
@ -132,13 +133,13 @@ func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model
repoInitCode := model.InitCodeFromModel(user.InitCode)
repoPhoneCode := model.PhoneCodeFromModel(user.PhoneCode)
createAggregates, err := UserCreateAggregate(ctx, es.AggregateCreator(), repoUser, repoInitCode, repoPhoneCode, resourceOwner)
createAggregates, err := UserCreateAggregate(ctx, es.AggregateCreator(), repoUser, repoInitCode, repoPhoneCode, resourceOwner, orgIamPolicy.UserLoginMustBeDomain)
return repoUser, createAggregates, err
}
func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy) (*usr_model.User, error) {
repoUser, aggregates, err := es.PrepareCreateUser(ctx, user, policy, "")
func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User, pwPolicy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy) (*usr_model.User, error) {
repoUser, aggregates, err := es.PrepareCreateUser(ctx, user, pwPolicy, orgIamPolicy, "")
if err != nil {
return nil, err
}
@ -152,8 +153,11 @@ func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User,
return model.UserToModel(repoUser), nil
}
func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
user.SetEmailAsUsername()
func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) {
err := user.CheckOrgIamPolicy(orgIamPolicy)
if err != nil {
return nil, nil, err
}
if !user.IsValid() || user.Password == nil || user.SecretString == "" {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Errors.User.InvalidData")
}
@ -175,12 +179,12 @@ func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_mod
repoUser := model.UserFromModel(user)
repoEmailCode := model.EmailCodeFromModel(user.EmailCode)
aggregates, err := UserRegisterAggregate(ctx, es.AggregateCreator(), repoUser, resourceOwner, repoEmailCode)
aggregates, err := UserRegisterAggregate(ctx, es.AggregateCreator(), repoUser, resourceOwner, repoEmailCode, orgIamPolicy.UserLoginMustBeDomain)
return repoUser, aggregates, err
}
func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*usr_model.User, error) {
repoUser, createAggregates, err := es.PrepareRegisterUser(ctx, user, policy, resourceOwner)
func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, pwPolicy *policy_model.PasswordComplexityPolicy, orgIamPolicy *org_model.OrgIamPolicy, resourceOwner string) (*usr_model.User, error) {
repoUser, createAggregates, err := es.PrepareRegisterUser(ctx, user, pwPolicy, orgIamPolicy, resourceOwner)
if err != nil {
return nil, err
}

View File

@ -2,13 +2,13 @@ package eventsourcing
import (
"context"
org_model "github.com/caos/zitadel/internal/org/model"
policy_model "github.com/caos/zitadel/internal/policy/model"
"encoding/json"
"net"
"testing"
"time"
policy_model "github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
"github.com/caos/zitadel/internal/api/auth"
@ -86,10 +86,11 @@ func TestUserByID(t *testing.T) {
func TestCreateUser(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *UserEventstore
ctx context.Context
user *model.User
policy *policy_model.PasswordComplexityPolicy
es *UserEventstore
ctx context.Context
user *model.User
policy *policy_model.PasswordComplexityPolicy
orgPolicy *org_model.OrgIamPolicy
}
type res struct {
user *model.User
@ -117,7 +118,8 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true,
},
},
policy: &policy_model.PasswordComplexityPolicy{},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -148,7 +150,8 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true,
},
},
policy: &policy_model.PasswordComplexityPolicy{},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{UserLoginMustBeDomain: false},
},
res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -184,7 +187,8 @@ func TestCreateUser(t *testing.T) {
IsPhoneVerified: true,
},
},
policy: &policy_model.PasswordComplexityPolicy{},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -221,7 +225,8 @@ func TestCreateUser(t *testing.T) {
IsEmailVerified: true,
},
},
policy: &policy_model.PasswordComplexityPolicy{},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
@ -239,6 +244,31 @@ func TestCreateUser(t *testing.T) {
},
{
name: "create user invalid",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "create user pw policy nil",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "create user org policy nil",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
@ -249,21 +279,10 @@ func TestCreateUser(t *testing.T) {
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "create user policy nil",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.CreateUser(tt.args.ctx, tt.args.user, tt.args.policy)
result, err := tt.args.es.CreateUser(tt.args.ctx, tt.args.user, tt.args.policy, tt.args.orgPolicy)
if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id")
@ -296,6 +315,7 @@ func TestRegisterUser(t *testing.T) {
user *model.User
resourceOwner string
policy *policy_model.PasswordComplexityPolicy
orgPolicy *org_model.OrgIamPolicy
}
type res struct {
user *model.User
@ -326,6 +346,7 @@ func TestRegisterUser(t *testing.T) {
},
},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{UserLoginMustBeDomain: true},
resourceOwner: "ResourceOwner",
},
res: res{
@ -359,6 +380,7 @@ func TestRegisterUser(t *testing.T) {
},
},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{UserLoginMustBeDomain: false},
resourceOwner: "ResourceOwner",
},
res: res{
@ -381,6 +403,7 @@ func TestRegisterUser(t *testing.T) {
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1}},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
resourceOwner: "ResourceOwner",
},
res: res{
@ -403,6 +426,7 @@ func TestRegisterUser(t *testing.T) {
},
},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
resourceOwner: "ResourceOwner",
},
res: res{
@ -424,14 +448,15 @@ func TestRegisterUser(t *testing.T) {
EmailAddress: "EmailAddress",
},
},
policy: &policy_model.PasswordComplexityPolicy{},
policy: &policy_model.PasswordComplexityPolicy{},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "no policy",
name: "no pw policy",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
@ -445,6 +470,28 @@ func TestRegisterUser(t *testing.T) {
EmailAddress: "EmailAddress",
},
},
orgPolicy: &org_model.OrgIamPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "no org policy",
args: args{
es: GetMockManipulateUser(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{Sequence: 1},
Profile: &model.Profile{
UserName: "EmailAddress",
FirstName: "FirstName",
LastName: "LastName",
},
Email: &model.Email{
EmailAddress: "EmailAddress",
},
},
policy: &policy_model.PasswordComplexityPolicy{},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
@ -453,7 +500,7 @@ func TestRegisterUser(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.RegisterUser(tt.args.ctx, tt.args.user, tt.args.policy, tt.args.resourceOwner)
result, err := tt.args.es.RegisterUser(tt.args.ctx, tt.args.user, tt.args.policy, tt.args.orgPolicy, tt.args.resourceOwner)
if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id")

View File

@ -5,7 +5,9 @@ import (
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"strings"
)
func UserByIDQuery(id string, latestSequence uint64) (*es_models.SearchQuery, error) {
@ -53,7 +55,7 @@ func UserAggregateOverwriteContext(ctx context.Context, aggCreator *es_models.Ag
return aggCreator.NewAggregate(ctx, user.AggregateID, model.UserAggregate, model.UserVersion, user.Sequence, es_models.OverwriteResourceOwner(resourceOwnerID), es_models.OverwriteEditorUser(userID))
}
func UserCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, initCode *model.InitUserCode, phoneCode *model.PhoneCode, resourceOwner string) (_ []*es_models.Aggregate, err error) {
func UserCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, initCode *model.InitUserCode, phoneCode *model.PhoneCode, resourceOwner string, userLoginMustBeDomain bool) (_ []*es_models.Aggregate, err error) {
if user == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user should not be nil")
}
@ -67,6 +69,14 @@ func UserCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCre
if err != nil {
return nil, err
}
if !userLoginMustBeDomain {
validationQuery := es_models.NewSearchQuery().
AggregateTypeFilter(org_es_model.OrgAggregate).
AggregateIDsFilter()
validation := addUserNameValidation(user.UserName)
agg.SetPrecondition(validationQuery, validation)
}
agg, err = agg.AppendEvent(model.UserAdded, user)
if err != nil {
@ -107,7 +117,7 @@ func UserCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCre
}, nil
}
func UserRegisterAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, resourceOwner string, emailCode *model.EmailCode) ([]*es_models.Aggregate, error) {
func UserRegisterAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, resourceOwner string, emailCode *model.EmailCode, userLoginMustBeDomain bool) ([]*es_models.Aggregate, error) {
if user == nil || resourceOwner == "" || emailCode == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user, resourceowner, emailcode should not be nothing")
}
@ -117,6 +127,14 @@ func UserRegisterAggregate(ctx context.Context, aggCreator *es_models.AggregateC
return nil, err
}
if !userLoginMustBeDomain {
validationQuery := es_models.NewSearchQuery().
AggregateTypeFilter(org_es_model.OrgAggregate).
AggregateIDsFilter()
validation := addUserNameValidation(user.UserName)
agg.SetPrecondition(validationQuery, validation)
}
agg, err = agg.AppendEvent(model.UserRegistered, user)
if err != nil {
return nil, err
@ -152,9 +170,9 @@ func getUniqueUserAggregates(ctx context.Context, aggCreator *es_models.Aggregat
}, nil
}
func reservedUniqueUserNameAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, resourceOwner, userName string) (*es_models.Aggregate, error) {
aggregate, err := aggCreator.NewAggregate(ctx, userName, model.UserUserNameAggregate, model.UserVersion, 0)
aggregate, err := aggCreator.NewAggregate(ctx, userName+resourceOwner, model.UserUserNameAggregate, model.UserVersion, 0)
if resourceOwner != "" {
aggregate, err = aggCreator.NewAggregate(ctx, userName, model.UserUserNameAggregate, model.UserVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
aggregate, err = aggCreator.NewAggregate(ctx, userName+resourceOwner, model.UserUserNameAggregate, model.UserVersion, 0, es_models.OverwriteResourceOwner(resourceOwner))
}
if err != nil {
return nil, err
@ -631,3 +649,44 @@ func isEventValidation(aggregate *es_models.Aggregate, eventType es_models.Event
return nil
}
}
func addUserNameValidation(userName string) func(...*es_models.Event) error {
return func(events ...*es_models.Event) error {
domains := make([]*org_es_model.OrgDomain, 0)
for _, event := range events {
switch event.Type {
case org_es_model.OrgDomainAdded:
domain := new(org_es_model.OrgDomain)
domain.SetData(event)
case org_es_model.OrgDomainVerified:
domain := new(org_es_model.OrgDomain)
domain.SetData(event)
for _, d := range domains {
if d.Domain == domain.Domain {
d.Verified = true
}
}
case org_es_model.OrgDomainRemoved:
domain := new(org_es_model.OrgDomain)
domain.SetData(event)
for i, d := range domains {
if d.Domain == domain.Domain {
domains[i] = domains[len(domains)-1]
domains[len(domains)-1] = nil
domains = domains[:len(domains)-1]
}
}
}
}
split := strings.Split(userName, "@")
if len(split) != 2 {
return nil
}
for _, d := range domains {
if d.Verified && d.Domain == split[1] {
return errors.ThrowPreconditionFailed(nil, "EVENT-us5Zw", "domain already reserved")
}
}
return nil
}
}

View File

@ -223,7 +223,7 @@ func TestUserCreateAggregate(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
aggregates, err := UserCreateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.initCode, tt.args.phoneCode, "")
aggregates, err := UserCreateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.initCode, tt.args.phoneCode, "", true)
if !tt.res.wantErr && len(aggregates) != tt.res.aggregatesLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.aggregatesLen, len(aggregates))
@ -348,7 +348,7 @@ func TestUserRegisterAggregate(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
aggregates, err := UserRegisterAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.resourceOwner, tt.args.emailCode)
aggregates, err := UserRegisterAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.resourceOwner, tt.args.emailCode, false)
if tt.res.errFunc == nil && len(aggregates[0].Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(aggregates[0].Events))

View File

@ -2,6 +2,7 @@ package model
import (
"encoding/json"
"github.com/lib/pq"
"time"
"github.com/caos/logging"
@ -23,39 +24,41 @@ const (
UserKeyEmail = "email"
UserKeyState = "user_state"
UserKeyResourceOwner = "resource_owner"
UserKeyLoginNames = "login_names"
)
type UserView struct {
ID string `json:"-" gorm:"column:id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
State int32 `json:"-" gorm:"column:user_state"`
PasswordSet bool `json:"-" gorm:"column:password_set"`
PasswordChangeRequired bool `json:"-" gorm:"column:password_change_required"`
PasswordChanged time.Time `json:"-" gorm:"column:password_change"`
LastLogin time.Time `json:"-" gorm:"column:last_login"`
UserName string `json:"userName" gorm:"column:user_name"`
FirstName string `json:"firstName" gorm:"column:first_name"`
LastName string `json:"lastName" gorm:"column:last_name"`
NickName string `json:"nickName" gorm:"column:nick_name"`
DisplayName string `json:"displayName" gorm:"column:display_name"`
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
Gender int32 `json:"gender" gorm:"column:gender"`
Email string `json:"email" gorm:"column:email"`
IsEmailVerified bool `json:"-" gorm:"column:is_email_verified"`
Phone string `json:"phone" gorm:"column:phone"`
IsPhoneVerified bool `json:"-" gorm:"column:is_phone_verified"`
Country string `json:"country" gorm:"column:country"`
Locality string `json:"locality" gorm:"column:locality"`
PostalCode string `json:"postalCode" gorm:"column:postal_code"`
Region string `json:"region" gorm:"column:region"`
StreetAddress string `json:"streetAddress" gorm:"column:street_address"`
OTPState int32 `json:"-" gorm:"column:otp_state"`
MfaMaxSetUp int32 `json:"-" gorm:"column:mfa_max_set_up"`
MfaInitSkipped time.Time `json:"-" gorm:"column:mfa_init_skipped"`
InitRequired bool `json:"-" gorm:"column:init_required"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
ID string `json:"-" gorm:"column:id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
State int32 `json:"-" gorm:"column:user_state"`
PasswordSet bool `json:"-" gorm:"column:password_set"`
PasswordChangeRequired bool `json:"-" gorm:"column:password_change_required"`
PasswordChanged time.Time `json:"-" gorm:"column:password_change"`
LastLogin time.Time `json:"-" gorm:"column:last_login"`
UserName string `json:"userName" gorm:"column:user_name"`
LoginNames pq.StringArray `json:"-" gorm:"column:login_names"`
FirstName string `json:"firstName" gorm:"column:first_name"`
LastName string `json:"lastName" gorm:"column:last_name"`
NickName string `json:"nickName" gorm:"column:nick_name"`
DisplayName string `json:"displayName" gorm:"column:display_name"`
PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"`
Gender int32 `json:"gender" gorm:"column:gender"`
Email string `json:"email" gorm:"column:email"`
IsEmailVerified bool `json:"-" gorm:"column:is_email_verified"`
Phone string `json:"phone" gorm:"column:phone"`
IsPhoneVerified bool `json:"-" gorm:"column:is_phone_verified"`
Country string `json:"country" gorm:"column:country"`
Locality string `json:"locality" gorm:"column:locality"`
PostalCode string `json:"postalCode" gorm:"column:postal_code"`
Region string `json:"region" gorm:"column:region"`
StreetAddress string `json:"streetAddress" gorm:"column:street_address"`
OTPState int32 `json:"-" gorm:"column:otp_state"`
MfaMaxSetUp int32 `json:"-" gorm:"column:mfa_max_set_up"`
MfaInitSkipped time.Time `json:"-" gorm:"column:mfa_init_skipped"`
InitRequired bool `json:"-" gorm:"column:init_required"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func UserFromModel(user *model.UserView) *UserView {
@ -70,6 +73,7 @@ func UserFromModel(user *model.UserView) *UserView {
PasswordChanged: user.PasswordChanged,
LastLogin: user.LastLogin,
UserName: user.UserName,
LoginNames: user.LoginNames,
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName,

View File

@ -69,6 +69,8 @@ func (key UserSearchKey) ToColumnName() string {
return UserKeyState
case usr_model.USERSEARCHKEY_RESOURCEOWNER:
return UserKeyResourceOwner
case usr_model.USERSEARCHKEY_LOGIN_NAMES:
return UserKeyLoginNames
default:
return ""
}

View File

@ -2,10 +2,12 @@ package view
import (
caos_errs "github.com/caos/zitadel/internal/errors"
global_model "github.com/caos/zitadel/internal/model"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/view"
"github.com/jinzhu/gorm"
"github.com/lib/pq"
)
func UserByID(db *gorm.DB, table, userID string) (*model.UserView, error) {
@ -28,6 +30,32 @@ func UserByUserName(db *gorm.DB, table, userName string) (*model.UserView, error
return user, err
}
func UserByLoginName(db *gorm.DB, table, loginName string) (*model.UserView, error) {
user := new(model.UserView)
loginNameQuery := &model.UserSearchQuery{
Key: usr_model.USERSEARCHKEY_LOGIN_NAMES,
Method: global_model.SEARCHMETHOD_EQUALS_IN_ARRAY,
Value: pq.Array([]string{loginName}),
}
query := view.PrepareGetByQuery(table, loginNameQuery)
err := query(db, user)
return user, err
}
func UsersByOrgID(db *gorm.DB, table, orgID string) ([]*model.UserView, error) {
users := make([]*model.UserView, 0)
orgIDQuery := &usr_model.UserSearchQuery{
Key: usr_model.USERSEARCHKEY_RESOURCEOWNER,
Method: global_model.SEARCHMETHOD_EQUALS,
Value: orgID,
}
query := view.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, int, error) {
users := make([]*model.UserView, 0)
query := view.PrepareSearchQuery(table, model.UserSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})

View File

@ -32,7 +32,6 @@ type UserGrantView struct {
Email string `json:"-" gorm:"column:email"`
ProjectName string `json:"-" gorm:"column:project_name"`
OrgName string `json:"-" gorm:"column:org_name"`
OrgDomain string `json:"-" gorm:"column:org_domain"`
RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
@ -57,7 +56,6 @@ func UserGrantFromModel(grant *model.UserGrantView) *UserGrantView {
Email: grant.Email,
ProjectName: grant.ProjectName,
OrgName: grant.OrgName,
OrgDomain: grant.OrgDomain,
RoleKeys: grant.RoleKeys,
Sequence: grant.Sequence,
}
@ -78,7 +76,6 @@ func UserGrantToModel(grant *UserGrantView) *model.UserGrantView {
Email: grant.Email,
ProjectName: grant.ProjectName,
OrgName: grant.OrgName,
OrgDomain: grant.OrgDomain,
RoleKeys: grant.RoleKeys,
Sequence: grant.Sequence,
}

View File

@ -104,6 +104,8 @@ func SetQuery(query *gorm.DB, key ColumnKey, value interface{}, method model.Sea
query = query.Where(column+" < ?", value)
case model.SEARCHMETHOD_IN:
query = query.Where(column+" IN (?)", value)
case model.SEARCHMETHOD_EQUALS_IN_ARRAY:
query = query.Where("? <@ "+column, value)
default:
return nil, nil
}

View File

@ -0,0 +1,7 @@
BEGIN;
ALTER TABLE auth.users ADD COLUMN login_names TEXT ARRAY;
ALTER TABLE management.users ADD COLUMN login_names TEXT ARRAY;
COMMIT;

View File

@ -0,0 +1,16 @@
BEGIN;
CREATE TABLE management.org_domains (
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
sequence BIGINT,
domain TEXT,
org_id TEXT,
verified BOOLEAN,
primary_domain BOOLEAN,
PRIMARY KEY (org_id, domain)
);
COMMIT;

View File

@ -34,6 +34,26 @@ var AdminService_AuthMethods = utils_auth.MethodMapping{
Permission: "iam.write",
CheckParam: "",
},
"/caos.zitadel.admin.api.v1.AdminService/GetOrgIamPolicy": utils_auth.Option{
Permission: "iam.policy.read",
CheckParam: "",
},
"/caos.zitadel.admin.api.v1.AdminService/CreateOrgIamPolicy": utils_auth.Option{
Permission: "iam.policy.write",
CheckParam: "",
},
"/caos.zitadel.admin.api.v1.AdminService/UpdateOrgIamPolicy": utils_auth.Option{
Permission: "iam.policy.write",
CheckParam: "",
},
"/caos.zitadel.admin.api.v1.AdminService/DeleteOrgIamPolicy": utils_auth.Option{
Permission: "iam.policy.delete",
CheckParam: "",
},
}
func AdminService_Authorization_Interceptor(verifier utils_auth.TokenVerifier, authConf *utils_auth.Config) grpc.UnaryServerInterceptor {

File diff suppressed because it is too large Load Diff

View File

@ -64,10 +64,7 @@ func request_AdminService_IsOrgUnique_0(ctx context.Context, marshaler runtime.M
var protoReq UniqueOrgRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AdminService_IsOrgUnique_0); err != nil {
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_AdminService_IsOrgUnique_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
@ -137,6 +134,130 @@ func request_AdminService_SetUpOrg_0(ctx context.Context, marshaler runtime.Mars
}
func request_AdminService_GetOrgIamPolicy_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OrgIamPolicyID
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["org_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id")
}
protoReq.OrgId, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
msg, err := client.GetOrgIamPolicy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_AdminService_CreateOrgIamPolicy_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OrgIamPolicyRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["org_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id")
}
protoReq.OrgId, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
msg, err := client.CreateOrgIamPolicy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_AdminService_UpdateOrgIamPolicy_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OrgIamPolicyRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["org_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id")
}
protoReq.OrgId, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
msg, err := client.UpdateOrgIamPolicy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_AdminService_DeleteOrgIamPolicy_0(ctx context.Context, marshaler runtime.Marshaler, client AdminServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq OrgIamPolicyID
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["org_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id")
}
protoReq.OrgId, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err)
}
msg, err := client.DeleteOrgIamPolicy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterAdminServiceHandlerFromEndpoint is same as RegisterAdminServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAdminServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@ -315,23 +436,111 @@ func RegisterAdminServiceHandlerClient(ctx context.Context, mux *runtime.ServeMu
})
mux.Handle("GET", pattern_AdminService_GetOrgIamPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AdminService_GetOrgIamPolicy_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AdminService_GetOrgIamPolicy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_AdminService_CreateOrgIamPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AdminService_CreateOrgIamPolicy_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AdminService_CreateOrgIamPolicy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_AdminService_UpdateOrgIamPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AdminService_UpdateOrgIamPolicy_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AdminService_UpdateOrgIamPolicy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_AdminService_DeleteOrgIamPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AdminService_DeleteOrgIamPolicy_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_AdminService_DeleteOrgIamPolicy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_AdminService_Healthz_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"healthz"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_Healthz_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"healthz"}, ""))
pattern_AdminService_Ready_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"ready"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_Ready_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"ready"}, ""))
pattern_AdminService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, ""))
pattern_AdminService_IsOrgUnique_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_isunique"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_IsOrgUnique_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_isunique"}, ""))
pattern_AdminService_GetOrgByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"orgs", "id"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_GetOrgByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"orgs", "id"}, ""))
pattern_AdminService_SearchOrgs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_search"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_SearchOrgs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_search"}, ""))
pattern_AdminService_SetUpOrg_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_setup"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AdminService_SetUpOrg_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"orgs", "_setup"}, ""))
pattern_AdminService_GetOrgIamPolicy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"orgs", "org_id", "iampolicy"}, ""))
pattern_AdminService_CreateOrgIamPolicy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"orgs", "org_id", "iampolicy"}, ""))
pattern_AdminService_UpdateOrgIamPolicy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"orgs", "org_id", "iampolicy"}, ""))
pattern_AdminService_DeleteOrgIamPolicy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 2, 2}, []string{"orgs", "org_id", "iampolicy"}, ""))
)
var (
@ -348,4 +557,12 @@ var (
forward_AdminService_SearchOrgs_0 = runtime.ForwardResponseMessage
forward_AdminService_SetUpOrg_0 = runtime.ForwardResponseMessage
forward_AdminService_GetOrgIamPolicy_0 = runtime.ForwardResponseMessage
forward_AdminService_CreateOrgIamPolicy_0 = runtime.ForwardResponseMessage
forward_AdminService_UpdateOrgIamPolicy_0 = runtime.ForwardResponseMessage
forward_AdminService_DeleteOrgIamPolicy_0 = runtime.ForwardResponseMessage
)

View File

@ -143,6 +143,113 @@
]
}
},
"/orgs/{org_id}/iampolicy": {
"get": {
"summary": "ORG_IAM_POLICY",
"operationId": "GetOrgIamPolicy",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1OrgIamPolicy"
}
}
},
"parameters": [
{
"name": "org_id",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"AdminService"
]
},
"delete": {
"operationId": "DeleteOrgIamPolicy",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
}
}
},
"parameters": [
{
"name": "org_id",
"in": "path",
"required": true,
"type": "string"
}
],
"tags": [
"AdminService"
]
},
"post": {
"operationId": "CreateOrgIamPolicy",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1OrgIamPolicy"
}
}
},
"parameters": [
{
"name": "org_id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1OrgIamPolicyRequest"
}
}
],
"tags": [
"AdminService"
]
},
"put": {
"operationId": "UpdateOrgIamPolicy",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/v1OrgIamPolicy"
}
}
},
"parameters": [
{
"name": "org_id",
"in": "path",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1OrgIamPolicyRequest"
}
}
],
"tags": [
"AdminService"
]
}
},
"/ready": {
"get": {
"summary": "Ready returns status OK as soon as all dependent services are available",
@ -167,7 +274,7 @@
"200": {
"description": "A successful response.",
"schema": {
"type": "object"
"$ref": "#/definitions/protobufStruct"
}
}
},
@ -178,6 +285,19 @@
}
},
"definitions": {
"protobufListValue": {
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"$ref": "#/definitions/protobufValue"
},
"description": "Repeated field of dynamically typed values."
}
},
"description": "`ListValue` is a wrapper around a repeated field of values.\n\nThe JSON representation for `ListValue` is JSON array."
},
"protobufNullValue": {
"type": "string",
"enum": [
@ -186,6 +306,51 @@
"default": "NULL_VALUE",
"description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value."
},
"protobufStruct": {
"type": "object",
"properties": {
"fields": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/protobufValue"
},
"description": "Unordered map of dynamically typed values."
}
},
"description": "`Struct` represents a structured data value, consisting of fields\nwhich map to dynamically typed values. In some languages, `Struct`\nmight be supported by a native representation. For example, in\nscripting languages like JS a struct is represented as an\nobject. The details of that representation are described together\nwith the proto support for the language.\n\nThe JSON representation for `Struct` is JSON object."
},
"protobufValue": {
"type": "object",
"properties": {
"null_value": {
"$ref": "#/definitions/protobufNullValue",
"description": "Represents a null value."
},
"number_value": {
"type": "number",
"format": "double",
"description": "Represents a double value."
},
"string_value": {
"type": "string",
"description": "Represents a string value."
},
"bool_value": {
"type": "boolean",
"format": "boolean",
"description": "Represents a boolean value."
},
"struct_value": {
"$ref": "#/definitions/protobufStruct",
"description": "Represents a structured value."
},
"list_value": {
"$ref": "#/definitions/protobufListValue",
"description": "Represents a repeated `Value`."
}
},
"description": "`Value` represents a dynamically typed value which can be either\nnull, a number, a string, a boolean, a recursive struct value, or a\nlist of values. A producer of value is expected to set one of that\nvariants, absence of any variant indicates an error.\n\nThe JSON representation for `Value` is JSON value."
},
"v1CreateOrgRequest": {
"type": "object",
"properties": {
@ -290,6 +455,52 @@
}
}
},
"v1OrgIamPolicy": {
"type": "object",
"properties": {
"org_id": {
"type": "string"
},
"description": {
"type": "string"
},
"user_login_must_be_domain": {
"type": "boolean",
"format": "boolean"
},
"default": {
"type": "boolean",
"format": "boolean"
},
"sequence": {
"type": "string",
"format": "uint64"
},
"creation_date": {
"type": "string",
"format": "date-time"
},
"change_date": {
"type": "string",
"format": "date-time"
}
}
},
"v1OrgIamPolicyRequest": {
"type": "object",
"properties": {
"org_id": {
"type": "string"
},
"description": {
"type": "string"
},
"user_login_must_be_domain": {
"type": "boolean",
"format": "boolean"
}
}
},
"v1OrgSearchKey": {
"type": "string",
"enum": [

View File

@ -37,6 +37,46 @@ func (m *MockAdminServiceClient) EXPECT() *MockAdminServiceClientMockRecorder {
return m.recorder
}
// CreateOrgIamPolicy mocks base method
func (m *MockAdminServiceClient) CreateOrgIamPolicy(arg0 context.Context, arg1 *grpc.OrgIamPolicyRequest, arg2 ...grpc0.CallOption) (*grpc.OrgIamPolicy, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "CreateOrgIamPolicy", varargs...)
ret0, _ := ret[0].(*grpc.OrgIamPolicy)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateOrgIamPolicy indicates an expected call of CreateOrgIamPolicy
func (mr *MockAdminServiceClientMockRecorder) CreateOrgIamPolicy(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrgIamPolicy", reflect.TypeOf((*MockAdminServiceClient)(nil).CreateOrgIamPolicy), varargs...)
}
// DeleteOrgIamPolicy mocks base method
func (m *MockAdminServiceClient) DeleteOrgIamPolicy(arg0 context.Context, arg1 *grpc.OrgIamPolicyID, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "DeleteOrgIamPolicy", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DeleteOrgIamPolicy indicates an expected call of DeleteOrgIamPolicy
func (mr *MockAdminServiceClientMockRecorder) DeleteOrgIamPolicy(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOrgIamPolicy", reflect.TypeOf((*MockAdminServiceClient)(nil).DeleteOrgIamPolicy), varargs...)
}
// GetOrgByID mocks base method
func (m *MockAdminServiceClient) GetOrgByID(arg0 context.Context, arg1 *grpc.OrgID, arg2 ...grpc0.CallOption) (*grpc.Org, error) {
m.ctrl.T.Helper()
@ -57,6 +97,26 @@ func (mr *MockAdminServiceClientMockRecorder) GetOrgByID(arg0, arg1 interface{},
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrgByID", reflect.TypeOf((*MockAdminServiceClient)(nil).GetOrgByID), varargs...)
}
// GetOrgIamPolicy mocks base method
func (m *MockAdminServiceClient) GetOrgIamPolicy(arg0 context.Context, arg1 *grpc.OrgIamPolicyID, arg2 ...grpc0.CallOption) (*grpc.OrgIamPolicy, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "GetOrgIamPolicy", varargs...)
ret0, _ := ret[0].(*grpc.OrgIamPolicy)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetOrgIamPolicy indicates an expected call of GetOrgIamPolicy
func (mr *MockAdminServiceClientMockRecorder) GetOrgIamPolicy(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrgIamPolicy", reflect.TypeOf((*MockAdminServiceClient)(nil).GetOrgIamPolicy), varargs...)
}
// Healthz mocks base method
func (m *MockAdminServiceClient) Healthz(arg0 context.Context, arg1 *emptypb.Empty, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
m.ctrl.T.Helper()
@ -157,6 +217,26 @@ func (mr *MockAdminServiceClientMockRecorder) SetUpOrg(arg0, arg1 interface{}, a
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUpOrg", reflect.TypeOf((*MockAdminServiceClient)(nil).SetUpOrg), varargs...)
}
// UpdateOrgIamPolicy mocks base method
func (m *MockAdminServiceClient) UpdateOrgIamPolicy(arg0 context.Context, arg1 *grpc.OrgIamPolicyRequest, arg2 ...grpc0.CallOption) (*grpc.OrgIamPolicy, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "UpdateOrgIamPolicy", varargs...)
ret0, _ := ret[0].(*grpc.OrgIamPolicy)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateOrgIamPolicy indicates an expected call of UpdateOrgIamPolicy
func (mr *MockAdminServiceClientMockRecorder) UpdateOrgIamPolicy(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOrgIamPolicy", reflect.TypeOf((*MockAdminServiceClient)(nil).UpdateOrgIamPolicy), varargs...)
}
// Validate mocks base method
func (m *MockAdminServiceClient) Validate(arg0 context.Context, arg1 *emptypb.Empty, arg2 ...grpc0.CallOption) (*structpb.Struct, error) {
m.ctrl.T.Helper()

View File

@ -2,10 +2,7 @@ package grpc
import (
"context"
"github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
"github.com/golang/protobuf/ptypes/empty"
)
func (s *Server) GetOrgByID(ctx context.Context, orgID *OrgID) (_ *Org, err error) {
@ -43,56 +40,31 @@ func (s *Server) SetUpOrg(ctx context.Context, orgSetUp *OrgSetUpRequest) (_ *Or
return setUpOrgResponseFromModel(setUp), err
}
func orgSearchRequestToModel(req *OrgSearchRequest) *org_model.OrgSearchRequest {
return &org_model.OrgSearchRequest{
Limit: req.Limit,
Asc: req.Asc,
Offset: req.Offset,
Queries: orgQueriesToModel(req.Queries),
SortingColumn: orgQueryKeyToModel(req.SortingColumn),
func (s *Server) GetOrgIamPolicy(ctx context.Context, in *OrgIamPolicyID) (_ *OrgIamPolicy, err error) {
policy, err := s.org.GetOrgIamPolicyByID(ctx, in.OrgId)
if err != nil {
return nil, err
}
return orgIamPolicyFromModel(policy), err
}
func orgQueriesToModel(queries []*OrgSearchQuery) []*org_model.OrgSearchQuery {
modelQueries := make([]*org_model.OrgSearchQuery, len(queries))
for i, query := range queries {
modelQueries[i] = orgQueryToModel(query)
func (s *Server) CreateOrgIamPolicy(ctx context.Context, in *OrgIamPolicyRequest) (_ *OrgIamPolicy, err error) {
policy, err := s.org.CreateOrgIamPolicy(ctx, orgIamPolicyRequestToModel(in))
if err != nil {
return nil, err
}
return modelQueries
return orgIamPolicyFromModel(policy), err
}
func orgQueryToModel(query *OrgSearchQuery) *org_model.OrgSearchQuery {
return &org_model.OrgSearchQuery{
Key: orgQueryKeyToModel(query.Key),
Value: query.Value,
Method: orgQueryMethodToModel(query.Method),
func (s *Server) UpdateOrgIamPolicy(ctx context.Context, in *OrgIamPolicyRequest) (_ *OrgIamPolicy, err error) {
policy, err := s.org.ChangeOrgIamPolicy(ctx, orgIamPolicyRequestToModel(in))
if err != nil {
return nil, err
}
return orgIamPolicyFromModel(policy), err
}
func orgQueryKeyToModel(key OrgSearchKey) org_model.OrgSearchKey {
switch key {
case OrgSearchKey_ORGSEARCHKEY_DOMAIN:
return org_model.ORGSEARCHKEY_ORG_DOMAIN
case OrgSearchKey_ORGSEARCHKEY_ORG_NAME:
return org_model.ORGSEARCHKEY_ORG_NAME
case OrgSearchKey_ORGSEARCHKEY_STATE:
return org_model.ORGSEARCHKEY_STATE
default:
return org_model.ORGSEARCHKEY_UNSPECIFIED
}
}
func orgQueryMethodToModel(method OrgSearchMethod) model.SearchMethod {
switch method {
case OrgSearchMethod_ORGSEARCHMETHOD_CONTAINS:
return model.SEARCHMETHOD_CONTAINS
case OrgSearchMethod_ORGSEARCHMETHOD_EQUALS:
return model.SEARCHMETHOD_EQUALS
case OrgSearchMethod_ORGSEARCHMETHOD_STARTS_WITH:
return model.SEARCHMETHOD_STARTS_WITH
default:
return 0
}
func (s *Server) DeleteOrgIamPolicy(ctx context.Context, in *OrgIamPolicyID) (_ *empty.Empty, err error) {
err = s.org.RemoveOrgIamPolicy(ctx, in.OrgId)
return &empty.Empty{}, err
}

View File

@ -3,6 +3,8 @@ package grpc
import (
"github.com/caos/logging"
admin_model "github.com/caos/zitadel/internal/admin/model"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/golang/protobuf/ptypes"
@ -18,8 +20,8 @@ func setUpRequestToModel(setUp *OrgSetUpRequest) *admin_model.SetupOrg {
func orgCreateRequestToModel(org *CreateOrgRequest) *org_model.Org {
return &org_model.Org{
Domain: org.Domain,
Name: org.Name,
Domains: []*org_model.OrgDomain{&org_model.OrgDomain{Domain: org.Domain}},
Name: org.Name,
}
}
@ -82,7 +84,6 @@ func orgFromModel(org *org_model.Org) *Org {
logging.Log("GRPC-dVnoj").OnError(err).Debug("unable to get timestamp from time")
return &Org{
Domain: org.Domain,
ChangeDate: changeDate,
CreationDate: creationDate,
Id: org.AggregateID,
@ -99,7 +100,6 @@ func orgViewFromModel(org *org_model.OrgView) *Org {
logging.Log("GRPC-dVnoj").OnError(err).Debug("unable to get timestamp from time")
return &Org{
Domain: org.Domain,
ChangeDate: changeDate,
CreationDate: creationDate,
Id: org.ID,
@ -196,3 +196,84 @@ func userStateFromModel(state usr_model.UserState) UserState {
return UserState_USERSTATE_UNSPECIFIED
}
}
func orgSearchRequestToModel(req *OrgSearchRequest) *org_model.OrgSearchRequest {
return &org_model.OrgSearchRequest{
Limit: req.Limit,
Asc: req.Asc,
Offset: req.Offset,
Queries: orgQueriesToModel(req.Queries),
SortingColumn: orgQueryKeyToModel(req.SortingColumn),
}
}
func orgQueriesToModel(queries []*OrgSearchQuery) []*org_model.OrgSearchQuery {
modelQueries := make([]*org_model.OrgSearchQuery, len(queries))
for i, query := range queries {
modelQueries[i] = orgQueryToModel(query)
}
return modelQueries
}
func orgQueryToModel(query *OrgSearchQuery) *org_model.OrgSearchQuery {
return &org_model.OrgSearchQuery{
Key: orgQueryKeyToModel(query.Key),
Value: query.Value,
Method: orgQueryMethodToModel(query.Method),
}
}
func orgQueryKeyToModel(key OrgSearchKey) org_model.OrgSearchKey {
switch key {
case OrgSearchKey_ORGSEARCHKEY_DOMAIN:
return org_model.ORGSEARCHKEY_ORG_DOMAIN
case OrgSearchKey_ORGSEARCHKEY_ORG_NAME:
return org_model.ORGSEARCHKEY_ORG_NAME
case OrgSearchKey_ORGSEARCHKEY_STATE:
return org_model.ORGSEARCHKEY_STATE
default:
return org_model.ORGSEARCHKEY_UNSPECIFIED
}
}
func orgQueryMethodToModel(method OrgSearchMethod) model.SearchMethod {
switch method {
case OrgSearchMethod_ORGSEARCHMETHOD_CONTAINS:
return model.SEARCHMETHOD_CONTAINS
case OrgSearchMethod_ORGSEARCHMETHOD_EQUALS:
return model.SEARCHMETHOD_EQUALS
case OrgSearchMethod_ORGSEARCHMETHOD_STARTS_WITH:
return model.SEARCHMETHOD_STARTS_WITH
default:
return 0
}
}
func orgIamPolicyFromModel(policy *org_model.OrgIamPolicy) *OrgIamPolicy {
creationDate, err := ptypes.TimestampProto(policy.CreationDate)
logging.Log("GRPC-ush36").OnError(err).Debug("unable to get timestamp from time")
changeDate, err := ptypes.TimestampProto(policy.ChangeDate)
logging.Log("GRPC-Ps9fW").OnError(err).Debug("unable to get timestamp from time")
return &OrgIamPolicy{
OrgId: policy.AggregateID,
Description: policy.Description,
UserLoginMustBeDomain: policy.UserLoginMustBeDomain,
Default: policy.Default,
CreationDate: creationDate,
ChangeDate: changeDate,
}
}
func orgIamPolicyRequestToModel(policy *OrgIamPolicyRequest) *org_model.OrgIamPolicy {
return &org_model.OrgIamPolicy{
ObjectRoot: models.ObjectRoot{
AggregateID: policy.OrgId,
},
Description: policy.Description,
UserLoginMustBeDomain: policy.UserLoginMustBeDomain,
}
}

View File

@ -98,6 +98,49 @@ service AdminService {
permission: "iam.write"
};
}
//ORG_IAM_POLICY
rpc GetOrgIamPolicy(OrgIamPolicyID) returns (OrgIamPolicy) {
option (google.api.http) = {
get: "/orgs/{org_id}/iampolicy"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "iam.policy.read"
};
}
rpc CreateOrgIamPolicy(OrgIamPolicyRequest) returns (OrgIamPolicy) {
option (google.api.http) = {
post: "/orgs/{org_id}/iampolicy"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "iam.policy.write"
};
}
rpc UpdateOrgIamPolicy(OrgIamPolicyRequest) returns (OrgIamPolicy) {
option (google.api.http) = {
put: "/orgs/{org_id}/iampolicy"
body: "*"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "iam.policy.write"
};
}
rpc DeleteOrgIamPolicy(OrgIamPolicyID) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/orgs/{org_id}/iampolicy"
};
option (caos.zitadel.utils.v1.auth_option) = {
permission: "iam.policy.delete"
};
}
}
message OrgID {
@ -235,5 +278,25 @@ enum Gender {
message CreateOrgRequest {
string name = 1 [(validate.rules).string.min_len = 1];
string domain = 2 [(validate.rules).string.min_len = 1];
string domain = 2;
}
message OrgIamPolicy {
string org_id = 1;
string description = 2;
bool user_login_must_be_domain = 3;
bool default = 4;
uint64 sequence = 5;
google.protobuf.Timestamp creation_date = 6;
google.protobuf.Timestamp change_date = 7;
}
message OrgIamPolicyRequest {
string org_id = 1;
string description = 2;
bool user_login_must_be_domain = 3;
}
message OrgIamPolicyID {
string org_id = 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -871,53 +871,53 @@ func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
}
var (
pattern_AuthService_Healthz_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"healthz"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_Healthz_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"healthz"}, ""))
pattern_AuthService_Ready_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"ready"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_Ready_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"ready"}, ""))
pattern_AuthService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_Validate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"validate"}, ""))
pattern_AuthService_GetMyUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"me", "usersessions"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"me", "usersessions"}, ""))
pattern_AuthService_GetMyUserProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "profile"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyUserProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "profile"}, ""))
pattern_AuthService_UpdateMyUserProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "profile"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_UpdateMyUserProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "profile"}, ""))
pattern_AuthService_GetMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "email"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "email"}, ""))
pattern_AuthService_ChangeMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "email"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_ChangeMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "email"}, ""))
pattern_AuthService_VerifyMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "email", "_verify"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_VerifyMyUserEmail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "email", "_verify"}, ""))
pattern_AuthService_ResendMyEmailVerificationMail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "email", "_resendverification"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_ResendMyEmailVerificationMail_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "email", "_resendverification"}, ""))
pattern_AuthService_GetMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "phone"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "phone"}, ""))
pattern_AuthService_ChangeMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "phone"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_ChangeMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "phone"}, ""))
pattern_AuthService_VerifyMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "phone", "_verify"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_VerifyMyUserPhone_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "phone", "_verify"}, ""))
pattern_AuthService_ResendMyPhoneVerificationCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "phone", "_resendverification"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_ResendMyPhoneVerificationCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "phone", "_resendverification"}, ""))
pattern_AuthService_GetMyUserAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "address"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyUserAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "address"}, ""))
pattern_AuthService_UpdateMyUserAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "address"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_UpdateMyUserAddress_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "address"}, ""))
pattern_AuthService_GetMyMfas_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "mfas"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyMfas_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"users", "me", "mfas"}, ""))
pattern_AuthService_ChangeMyPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "passwords", "_change"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_ChangeMyPassword_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "passwords", "_change"}, ""))
pattern_AuthService_AddMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_AddMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, ""))
pattern_AuthService_VerifyMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"users", "me", "mfa", "otp", "_verify"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_VerifyMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"users", "me", "mfa", "otp", "_verify"}, ""))
pattern_AuthService_RemoveMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_RemoveMfaOTP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"users", "me", "mfa", "otp"}, ""))
pattern_AuthService_SearchMyUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"usergrants", "me", "_search"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_SearchMyUserGrant_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"usergrants", "me", "_search"}, ""))
pattern_AuthService_SearchMyProjectOrgs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"global", "projectorgs", "_search"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_SearchMyProjectOrgs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"global", "projectorgs", "_search"}, ""))
pattern_AuthService_GetMyZitadelPermissions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"permissions", "zitadel", "me"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_AuthService_GetMyZitadelPermissions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"permissions", "zitadel", "me"}, ""))
)
var (

Some files were not shown because too many files have changed in this diff Show More