From 3025ac577b565c21e0ba87f095d2361b1df9d3c5 Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 26 May 2020 16:46:16 +0200 Subject: [PATCH] feat: org queries (#136) * search orgs * org by domain * member spooler * member * get roles * tests * types duration * use default func for renew * correct database * reorder migrations * delete unused consts * move get roles to internal * use prepared org by domain * implement org in other objects * add eventstores --- cmd/zitadel/startup.yaml | 11 ++ .../eventsourcing/eventstore/org.go | 21 ++- .../eventsourcing/handler/handler.go | 43 ++++++ .../repository/eventsourcing/handler/org.go | 65 +++++++++ .../repository/eventsourcing/repository.go | 36 +++-- .../repository/eventsourcing/spooler/lock.go | 29 ++++ .../eventsourcing/spooler/lock_test.go | 127 ++++++++++++++++++ .../eventsourcing/spooler/spooler.go | 29 ++++ .../eventsourcing/view/error_event.go | 17 +++ .../repository/eventsourcing/view/org.go | 43 ++++++ .../repository/eventsourcing/view/sequence.go | 17 +++ .../repository/eventsourcing/view/view.go | 25 ++++ internal/admin/repository/org.go | 2 +- .../eventsourcing/eventstore/org.go | 38 +++++- .../eventsourcing/eventstore/org_member.go | 30 ----- .../eventsourcing/eventstore/project.go | 23 ++++ .../eventsourcing/handler/granted_project.go | 22 ++- .../eventsourcing/handler/handler.go | 15 ++- .../repository/eventsourcing/handler/org.go | 65 +++++++++ .../eventsourcing/handler/org_member.go | 127 ++++++++++++++++++ .../eventsourcing/handler/user_grant.go | 22 ++- .../repository/eventsourcing/org.go | 52 ------- .../repository/eventsourcing/repository.go | 8 +- .../repository/eventsourcing/view/org.go | 42 ++++++ .../eventsourcing/view/org_member.go | 55 ++++++++ internal/management/repository/org.go | 7 +- internal/management/repository/project.go | 3 + internal/management/repository/repository.go | 1 - internal/org/model/org_member_view.go | 59 ++++++++ internal/org/model/org_view.go | 73 ++++++++++ internal/org/repository/view/org.go | 101 ++++++++++++++ internal/org/repository/view/org_member.go | 99 ++++++++++++++ .../org/repository/view/org_member_query.go | 69 ++++++++++ .../org/repository/view/org_member_view.go | 58 ++++++++ internal/org/repository/view/org_query.go | 67 +++++++++ internal/org/repository/view/org_view.go | 41 ++++++ .../repository/eventsourcing/eventstore.go | 21 ++- .../repository/view/model/user_grant.go | 3 +- migrations/cockroach/V1.10__mgmt_orgs.sql | 34 +++++ migrations/cockroach/V1.8__admin.sql | 49 +++++++ migrations/cockroach/V1.9__admin_grant.sql | 6 + pkg/admin/api/grpc/org.go | 69 +++++++++- pkg/admin/api/grpc/org_converter.go | 21 ++- pkg/management/api/grpc/org.go | 2 +- pkg/management/api/grpc/org_converter.go | 17 +++ pkg/management/api/grpc/org_member.go | 15 ++- .../api/grpc/org_member_converter.go | 94 +++++++++++++ pkg/management/api/grpc/project_grant.go | 6 +- .../api/grpc/project_grant_member.go | 5 + pkg/management/api/grpc/project_member.go | 4 +- pkg/management/api/grpc/server.go | 2 - pkg/management/management.go | 6 +- 52 files changed, 1732 insertions(+), 164 deletions(-) create mode 100644 internal/admin/repository/eventsourcing/handler/handler.go create mode 100644 internal/admin/repository/eventsourcing/handler/org.go create mode 100644 internal/admin/repository/eventsourcing/spooler/lock.go create mode 100644 internal/admin/repository/eventsourcing/spooler/lock_test.go create mode 100644 internal/admin/repository/eventsourcing/spooler/spooler.go create mode 100644 internal/admin/repository/eventsourcing/view/error_event.go create mode 100644 internal/admin/repository/eventsourcing/view/org.go create mode 100644 internal/admin/repository/eventsourcing/view/sequence.go create mode 100644 internal/admin/repository/eventsourcing/view/view.go delete mode 100644 internal/management/repository/eventsourcing/eventstore/org_member.go create mode 100644 internal/management/repository/eventsourcing/handler/org.go create mode 100644 internal/management/repository/eventsourcing/handler/org_member.go delete mode 100644 internal/management/repository/eventsourcing/org.go create mode 100644 internal/management/repository/eventsourcing/view/org.go create mode 100644 internal/management/repository/eventsourcing/view/org_member.go create mode 100644 internal/org/model/org_member_view.go create mode 100644 internal/org/model/org_view.go create mode 100644 internal/org/repository/view/org.go create mode 100644 internal/org/repository/view/org_member.go create mode 100644 internal/org/repository/view/org_member_query.go create mode 100644 internal/org/repository/view/org_member_view.go create mode 100644 internal/org/repository/view/org_query.go create mode 100644 internal/org/repository/view/org_view.go create mode 100644 migrations/cockroach/V1.10__mgmt_orgs.sql create mode 100644 migrations/cockroach/V1.8__admin.sql create mode 100644 migrations/cockroach/V1.9__admin_grant.sql diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 659d16b987..accf263ef1 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -93,6 +93,7 @@ Admin: CustomHeaders: - x-zitadel- Repository: + SearchLimit: 100 Eventstore: ServiceName: 'Admin' Repository: @@ -106,6 +107,16 @@ Admin: Type: 'fastcache' Config: MaxCacheSizeInByte: 10485760 #10mb + View: + Host: $ZITADEL_EVENTSTORE_HOST + Port: $ZITADEL_EVENTSTORE_PORT + User: 'admin_api' + Database: 'admin_api' + SSLmode: disable + Spooler: + ConcurrentTasks: 1 + BulkLimit: 100 + FailureCountUntilSkip: 5 Console: Port: 50050 diff --git a/internal/admin/repository/eventsourcing/eventstore/org.go b/internal/admin/repository/eventsourcing/eventstore/org.go index 7774aef0e6..5082532e9c 100644 --- a/internal/admin/repository/eventsourcing/eventstore/org.go +++ b/internal/admin/repository/eventsourcing/eventstore/org.go @@ -4,11 +4,12 @@ import ( "context" admin_model "github.com/caos/zitadel/internal/admin/model" - "github.com/caos/zitadel/internal/errors" + admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" "github.com/caos/zitadel/internal/eventstore" "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" usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing" ) @@ -16,6 +17,10 @@ type OrgRepo struct { Eventstore eventstore.Eventstore OrgEventstore *org_es.OrgEventstore UserEventstore *usr_es.UserEventstore + + View *admin_view.View + + SearchLimit uint64 } func (repo *OrgRepo) SetUpOrg(ctx context.Context, setUp *admin_model.SetupOrg) (*admin_model.SetupOrg, error) { @@ -51,8 +56,18 @@ func (repo *OrgRepo) OrgByID(ctx context.Context, id string) (*org_model.Org, er return repo.OrgEventstore.OrgByID(ctx, org_model.NewOrg(id)) } -func (repo *OrgRepo) SearchOrgs(ctx context.Context) ([]*org_model.Org, error) { - return nil, errors.ThrowUnimplemented(nil, "EVENT-hFIHK", "search not implemented") +func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) { + query.EnsureLimit(repo.SearchLimit) + orgs, count, err := repo.View.SearchOrgs(query) + if err != nil { + return nil, err + } + return &org_model.OrgSearchResult{ + Offset: query.Offset, + Limit: query.Limit, + TotalResult: uint64(count), + Result: org_view.OrgsToModel(orgs), + }, nil } func (repo *OrgRepo) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) { diff --git a/internal/admin/repository/eventsourcing/handler/handler.go b/internal/admin/repository/eventsourcing/handler/handler.go new file mode 100644 index 0000000000..b12ef95bc0 --- /dev/null +++ b/internal/admin/repository/eventsourcing/handler/handler.go @@ -0,0 +1,43 @@ +package handler + +import ( + "time" + + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" + "github.com/caos/zitadel/internal/config/types" + "github.com/caos/zitadel/internal/eventstore/spooler" + proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" + usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" +) + +type Configs map[string]*Config + +type Config struct { + MinimumCycleDuration types.Duration +} + +type handler struct { + view *view.View + bulkLimit uint64 + cycleDuration time.Duration + errorCountUntilSkip uint64 +} + +type EventstoreRepos struct { + ProjectEvents *proj_event.ProjectEventstore + UserEvents *usr_event.UserEventstore +} + +func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View) []spooler.Handler { + return []spooler.Handler{ + &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, + } +} + +func (configs Configs) cycleDuration(viewModel string) time.Duration { + c, ok := configs[viewModel] + if !ok { + return 1 * time.Second + } + return c.MinimumCycleDuration.Duration +} diff --git a/internal/admin/repository/eventsourcing/handler/org.go b/internal/admin/repository/eventsourcing/handler/org.go new file mode 100644 index 0000000000..5564e4eb94 --- /dev/null +++ b/internal/admin/repository/eventsourcing/handler/org.go @@ -0,0 +1,65 @@ +package handler + +import ( + "time" + + "github.com/caos/logging" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/spooler" + org_model "github.com/caos/zitadel/internal/org/model" + "github.com/caos/zitadel/internal/org/repository/eventsourcing" + "github.com/caos/zitadel/internal/org/repository/view" +) + +type Org struct { + handler +} + +const ( + orgTable = "admin_api.orgs" +) + +func (o *Org) MinimumCycleDuration() time.Duration { return o.cycleDuration } + +func (o *Org) ViewModel() string { + return orgTable +} + +func (o *Org) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := o.view.GetLatestOrgSequence() + if err != nil { + return nil, err + } + return eventsourcing.OrgQuery(sequence), nil +} + +func (o *Org) Process(event *es_models.Event) error { + org := new(view.OrgView) + + switch event.Type { + case org_model.OrgAdded: + org.AppendEvent(event) + case org_model.OrgChanged: + err := org.SetData(event) + if err != nil { + return err + } + org, err = o.view.OrgByID(org.ID) + if err != nil { + return err + } + err = org.AppendEvent(event) + if err != nil { + return err + } + default: + return o.view.ProcessedOrgSequence(event.Sequence) + } + + return o.view.PutOrg(org) +} + +func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { + logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in project app handler") + return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip) +} diff --git a/internal/admin/repository/eventsourcing/repository.go b/internal/admin/repository/eventsourcing/repository.go index 8851f9eb55..18c75bc091 100644 --- a/internal/admin/repository/eventsourcing/repository.go +++ b/internal/admin/repository/eventsourcing/repository.go @@ -6,8 +6,12 @@ import ( "github.com/caos/logging" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "github.com/caos/zitadel/internal/admin/repository/eventsourcing/setup" + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/spooler" + admin_view "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" sd "github.com/caos/zitadel/internal/config/systemdefaults" + "github.com/caos/zitadel/internal/config/types" es_int "github.com/caos/zitadel/internal/eventstore" + es_spol "github.com/caos/zitadel/internal/eventstore/spooler" es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing" es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing" es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing" @@ -15,13 +19,14 @@ import ( ) type Config struct { - Eventstore es_int.Config - //View view.ViewConfig - //Spooler spooler.SpoolerConfig + SearchLimit uint64 + Eventstore es_int.Config + View types.SQL + Spooler spooler.SpoolerConfig } type EsRepository struct { - //spooler *es_spooler.Spooler + spooler *es_spol.Spooler eventstore.OrgRepo } @@ -31,15 +36,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) ( return nil, err } - //view, sql, err := mgmt_view.StartView(conf.View) - //if err != nil { - // return nil, err - //} - - //conf.Spooler.View = view - //conf.Spooler.EsClient = es.Client - //conf.Spooler.SQL = sql - //spool := spooler.StartSpooler(conf.Spooler) iam, err := es_iam.StartIam(es_iam.IamConfig{ Eventstore: es, Cache: conf.Eventstore.Cache, @@ -66,15 +62,29 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) ( return nil, err } + sqlClient, err := conf.View.Start() + if err != nil { + return nil, err + } + view, err := admin_view.StartView(sqlClient) + if err != nil { + return nil, err + } + eventstoreRepos := setup.EventstoreRepos{OrgEvents: org, UserEvents: user, ProjectEvents: project, IamEvents: iam} err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx) logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup") + spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient) + return &EsRepository{ + spooler: spool, OrgRepo: eventstore.OrgRepo{ Eventstore: es, OrgEventstore: org, UserEventstore: user, + View: view, + SearchLimit: conf.SearchLimit, }, }, nil } diff --git a/internal/admin/repository/eventsourcing/spooler/lock.go b/internal/admin/repository/eventsourcing/spooler/lock.go new file mode 100644 index 0000000000..f3823015e2 --- /dev/null +++ b/internal/admin/repository/eventsourcing/spooler/lock.go @@ -0,0 +1,29 @@ +package spooler + +import ( + "database/sql" + "time" + + es_locker "github.com/caos/zitadel/internal/eventstore/locker" +) + +const ( + lockTable = "admin_api.locks" + lockedUntilKey = "locked_until" + lockerIDKey = "locker_id" + objectTypeKey = "object_type" +) + +type locker struct { + dbClient *sql.DB +} + +type lock struct { + LockerID string `gorm:"column:locker_id;primary_key"` + LockedUntil time.Time `gorm:"column:locked_until"` + ViewName string `gorm:"column:object_type;primary_key"` +} + +func (l *locker) Renew(lockerID, viewModel string, waitTime time.Duration) error { + return es_locker.Renew(l.dbClient, lockTable, lockerID, viewModel, waitTime) +} diff --git a/internal/admin/repository/eventsourcing/spooler/lock_test.go b/internal/admin/repository/eventsourcing/spooler/lock_test.go new file mode 100644 index 0000000000..bb1c77ddfb --- /dev/null +++ b/internal/admin/repository/eventsourcing/spooler/lock_test.go @@ -0,0 +1,127 @@ +package spooler + +import ( + "database/sql" + "testing" + "time" + + "github.com/DATA-DOG/go-sqlmock" +) + +type dbMock struct { + db *sql.DB + mock sqlmock.Sqlmock +} + +func mockDB(t *testing.T) *dbMock { + mockDB := dbMock{} + var err error + mockDB.db, mockDB.mock, err = sqlmock.New() + if err != nil { + t.Fatalf("error occured while creating stub db %v", err) + } + + mockDB.mock.MatchExpectationsInOrder(true) + + return &mockDB +} + +func (db *dbMock) expectCommit() *dbMock { + db.mock.ExpectCommit() + + return db +} + +func (db *dbMock) expectRollback() *dbMock { + db.mock.ExpectRollback() + + return db +} + +func (db *dbMock) expectBegin() *dbMock { + db.mock.ExpectBegin() + + return db +} + +func (db *dbMock) expectSavepoint() *dbMock { + db.mock.ExpectExec("SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1)) + return db +} + +func (db *dbMock) expectReleaseSavepoint() *dbMock { + db.mock.ExpectExec("RELEASE SAVEPOINT").WillReturnResult(sqlmock.NewResult(1, 1)) + + return db +} + +func (db *dbMock) expectRenew(lockerID, view string, affectedRows int64) *dbMock { + query := db.mock. + ExpectExec(`INSERT INTO admin_api\.locks \(object_type, locker_id, locked_until\) VALUES \(\$1, \$2, now\(\)\+\$3\) ON CONFLICT \(object_type\) DO UPDATE SET locked_until = now\(\)\+\$4, locker_id = \$5 WHERE \(locks\.locked_until < now\(\) OR locks\.locker_id = \$6\) AND locks\.object_type = \$7`). + WithArgs(view, lockerID, sqlmock.AnyArg(), sqlmock.AnyArg(), lockerID, lockerID, view). + WillReturnResult(sqlmock.NewResult(1, 1)) + + if affectedRows == 0 { + query.WillReturnResult(sqlmock.NewResult(0, 0)) + } else { + query.WillReturnResult(sqlmock.NewResult(1, affectedRows)) + } + + return db +} + +func Test_locker_Renew(t *testing.T) { + type fields struct { + db *dbMock + } + type args struct { + lockerID string + viewModel string + waitTime time.Duration + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "renew succeeded", + fields: fields{ + db: mockDB(t). + expectBegin(). + expectSavepoint(). + expectRenew("locker", "view", 1). + expectReleaseSavepoint(). + expectCommit(), + }, + args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second}, + wantErr: false, + }, + { + name: "renew now rows updated", + fields: fields{ + db: mockDB(t). + expectBegin(). + expectSavepoint(). + expectRenew("locker", "view", 0). + expectRollback(), + }, + args: args{lockerID: "locker", viewModel: "view", waitTime: 1 * time.Second}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + l := &locker{ + dbClient: tt.fields.db.db, + } + if err := l.Renew(tt.args.lockerID, tt.args.viewModel, tt.args.waitTime); (err != nil) != tt.wantErr { + t.Errorf("locker.Renew() error = %v, wantErr %v", err, tt.wantErr) + } + if err := tt.fields.db.mock.ExpectationsWereMet(); err != nil { + t.Errorf("not all database expectations met: %v", err) + } + }) + } +} diff --git a/internal/admin/repository/eventsourcing/spooler/spooler.go b/internal/admin/repository/eventsourcing/spooler/spooler.go new file mode 100644 index 0000000000..0929070b0c --- /dev/null +++ b/internal/admin/repository/eventsourcing/spooler/spooler.go @@ -0,0 +1,29 @@ +package spooler + +import ( + "database/sql" + + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/handler" + "github.com/caos/zitadel/internal/admin/repository/eventsourcing/view" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/spooler" +) + +type SpoolerConfig struct { + BulkLimit uint64 + FailureCountUntilSkip uint64 + ConcurrentTasks int + Handlers handler.Configs +} + +func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB) *spooler.Spooler { + spoolerConfig := spooler.Config{ + Eventstore: es, + Locker: &locker{dbClient: sql}, + ConcurrentTasks: c.ConcurrentTasks, + ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view), + } + spool := spoolerConfig.New() + spool.Start() + return spool +} diff --git a/internal/admin/repository/eventsourcing/view/error_event.go b/internal/admin/repository/eventsourcing/view/error_event.go new file mode 100644 index 0000000000..7f3c9ff3fb --- /dev/null +++ b/internal/admin/repository/eventsourcing/view/error_event.go @@ -0,0 +1,17 @@ +package view + +import ( + "github.com/caos/zitadel/internal/view" +) + +const ( + errTable = "admin_api.failed_event" +) + +func (v *View) saveFailedEvent(failedEvent *view.FailedEvent) error { + return view.SaveFailedEvent(v.Db, errTable, failedEvent) +} + +func (v *View) latestFailedEvent(viewName string, sequence uint64) (*view.FailedEvent, error) { + return view.LatestFailedEvent(v.Db, errTable, viewName, sequence) +} diff --git a/internal/admin/repository/eventsourcing/view/org.go b/internal/admin/repository/eventsourcing/view/org.go new file mode 100644 index 0000000000..e74c9a09ec --- /dev/null +++ b/internal/admin/repository/eventsourcing/view/org.go @@ -0,0 +1,43 @@ +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/view" +) + +const ( + orgTable = "admin_api.orgs" +) + +func (v *View) OrgByID(orgID string) (*org_view.OrgView, error) { + return org_view.OrgByID(v.Db, orgTable, orgID) +} + +func (v *View) SearchOrgs(query *org_model.OrgSearchRequest) ([]*org_view.OrgView, int, error) { + return org_view.SearchOrgs(v.Db, orgTable, query) +} + +func (v *View) PutOrg(org *org_view.OrgView) error { + err := org_view.PutOrg(v.Db, orgTable, org) + if err != nil { + return err + } + return v.ProcessedOrgSequence(org.Sequence) +} + +func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*view.FailedEvent, error) { + return v.latestFailedEvent(orgTable, sequence) +} + +func (v *View) ProcessedOrgFailedEvent(failedEvent *view.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} + +func (v *View) GetLatestOrgSequence() (uint64, error) { + return v.latestSequence(orgTable) +} + +func (v *View) ProcessedOrgSequence(eventSequence uint64) error { + return v.saveCurrentSequence(orgTable, eventSequence) +} diff --git a/internal/admin/repository/eventsourcing/view/sequence.go b/internal/admin/repository/eventsourcing/view/sequence.go new file mode 100644 index 0000000000..7f5db354a3 --- /dev/null +++ b/internal/admin/repository/eventsourcing/view/sequence.go @@ -0,0 +1,17 @@ +package view + +import ( + "github.com/caos/zitadel/internal/view" +) + +const ( + sequencesTable = "admin_api.current_sequences" +) + +func (v *View) saveCurrentSequence(viewName string, sequence uint64) error { + return view.SaveCurrentSequence(v.Db, sequencesTable, viewName, sequence) +} + +func (v *View) latestSequence(viewName string) (uint64, error) { + return view.LatestSequence(v.Db, sequencesTable, viewName) +} diff --git a/internal/admin/repository/eventsourcing/view/view.go b/internal/admin/repository/eventsourcing/view/view.go new file mode 100644 index 0000000000..4b8c52392d --- /dev/null +++ b/internal/admin/repository/eventsourcing/view/view.go @@ -0,0 +1,25 @@ +package view + +import ( + "database/sql" + + "github.com/jinzhu/gorm" +) + +type View struct { + Db *gorm.DB +} + +func StartView(sqlClient *sql.DB) (*View, error) { + gorm, err := gorm.Open("postgres", sqlClient) + if err != nil { + return nil, err + } + return &View{ + Db: gorm, + }, nil +} + +func (v *View) Health() (err error) { + return v.Db.DB().Ping() +} diff --git a/internal/admin/repository/org.go b/internal/admin/repository/org.go index c3a1a351c1..a929abcd47 100644 --- a/internal/admin/repository/org.go +++ b/internal/admin/repository/org.go @@ -11,5 +11,5 @@ type OrgRepository interface { SetUpOrg(context.Context, *admin_model.SetupOrg) (*admin_model.SetupOrg, error) IsOrgUnique(ctx context.Context, name, domain string) (bool, error) OrgByID(ctx context.Context, id string) (*org_model.Org, error) - SearchOrgs(ctx context.Context) ([]*org_model.Org, error) + SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) } diff --git a/internal/management/repository/eventsourcing/eventstore/org.go b/internal/management/repository/eventsourcing/eventstore/org.go index 92b9a3c565..13319b03ad 100644 --- a/internal/management/repository/eventsourcing/eventstore/org.go +++ b/internal/management/repository/eventsourcing/eventstore/org.go @@ -2,14 +2,20 @@ package eventstore import ( "context" + "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 { + SearchLimit uint64 *org_es.OrgEventstore + View *mgmt_view.View + Roles []string } func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.Org, error) { @@ -17,8 +23,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.Org, error) { - return nil, errors.ThrowUnimplemented(nil, "EVENT-GQoS8", "not implemented") +func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error) { + org, err := repo.View.OrgByDomain(domain) + if err != nil { + return nil, err + } + return view.OrgToModel(org), nil } func (repo *OrgRepository) UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error) { @@ -50,3 +60,27 @@ func (repo *OrgRepository) RemoveOrgMember(ctx context.Context, orgID, userID st member := org_model.NewOrgMember(orgID, userID) return repo.OrgEventstore.RemoveOrgMember(ctx, member) } + +func (repo *OrgRepository) SearchOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) { + request.EnsureLimit(repo.SearchLimit) + members, count, err := repo.View.SearchOrgMembers(request) + if err != nil { + return nil, err + } + return &org_model.OrgMemberSearchResponse{ + Offset: request.Offset, + Limit: request.Limit, + TotalResult: uint64(count), + Result: view.OrgMembersToModel(members), + }, nil +} + +func (repo *OrgRepository) GetOrgMemberRoles() []string { + roles := make([]string, 0) + for _, roleMap := range repo.Roles { + if strings.HasPrefix(roleMap, "ORG") { + roles = append(roles, roleMap) + } + } + return roles +} diff --git a/internal/management/repository/eventsourcing/eventstore/org_member.go b/internal/management/repository/eventsourcing/eventstore/org_member.go deleted file mode 100644 index dcddee530b..0000000000 --- a/internal/management/repository/eventsourcing/eventstore/org_member.go +++ /dev/null @@ -1,30 +0,0 @@ -package eventstore - -import ( - "context" - - org_model "github.com/caos/zitadel/internal/org/model" - org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" -) - -type OrgMemberRepository struct { - *org_es.OrgEventstore -} - -func (repo *OrgMemberRepository) OrgMemberByID(ctx context.Context, orgID, userID string) (member *org_model.OrgMember, err error) { - member = org_model.NewOrgMember(orgID, userID) - return repo.OrgEventstore.OrgMemberByIDs(ctx, member) -} - -func (repo *OrgMemberRepository) AddOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) { - return repo.OrgEventstore.AddOrgMember(ctx, member) -} - -func (repo *OrgMemberRepository) ChangeOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) { - return repo.OrgEventstore.ChangeOrgMember(ctx, member) -} - -func (repo *OrgMemberRepository) RemoveOrgMember(ctx context.Context, orgID, userID string) error { - member := org_model.NewOrgMember(orgID, userID) - return repo.OrgEventstore.RemoveOrgMember(ctx, member) -} diff --git a/internal/management/repository/eventsourcing/eventstore/project.go b/internal/management/repository/eventsourcing/eventstore/project.go index 8bb890dc9a..810c90d22b 100644 --- a/internal/management/repository/eventsourcing/eventstore/project.go +++ b/internal/management/repository/eventsourcing/eventstore/project.go @@ -2,6 +2,8 @@ package eventstore import ( "context" + "strings" + "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "github.com/caos/zitadel/internal/project/repository/view/model" @@ -13,6 +15,7 @@ type ProjectRepo struct { SearchLimit uint64 ProjectEvents *proj_event.ProjectEventstore View *view.View + Roles []string } func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *proj_model.Project, err error) { @@ -212,3 +215,23 @@ func (repo *ProjectRepo) SearchProjectGrantMembers(ctx context.Context, request Result: model.ProjectGrantMembersToModel(members), }, nil } + +func (repo *ProjectRepo) GetProjectMemberRoles() []string { + roles := make([]string, 0) + for _, roleMap := range repo.Roles { + if strings.HasPrefix(roleMap, "PROJECT") && !strings.HasPrefix(roleMap, "PROJECT_GRANT") { + roles = append(roles, roleMap) + } + } + return roles +} + +func (repo *ProjectRepo) GetProjectGrantMemberRoles() []string { + roles := make([]string, 0) + for _, roleMap := range repo.Roles { + if strings.HasPrefix(roleMap, "PROJECT_GRANT") { + roles = append(roles, roleMap) + } + } + return roles +} diff --git a/internal/management/repository/eventsourcing/handler/granted_project.go b/internal/management/repository/eventsourcing/handler/granted_project.go index 14d51a0325..c7f9237486 100644 --- a/internal/management/repository/eventsourcing/handler/granted_project.go +++ b/internal/management/repository/eventsourcing/handler/granted_project.go @@ -9,8 +9,9 @@ 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/project/model" - "github.com/caos/zitadel/internal/project/repository/eventsourcing" + org_model "github.com/caos/zitadel/internal/org/model" + org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" + proj_model "github.com/caos/zitadel/internal/project/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model" @@ -20,6 +21,7 @@ type GrantedProject struct { handler eventstore eventstore.Eventstore projectEvents *proj_event.ProjectEventstore + orgEvents *org_event.OrgEventstore } const ( @@ -37,7 +39,7 @@ func (p *GrantedProject) EventQuery() (*models.SearchQuery, error) { if err != nil { return nil, err } - return eventsourcing.ProjectQuery(sequence), nil + return proj_event.ProjectQuery(sequence), nil } func (p *GrantedProject) Process(event *models.Event) (err error) { @@ -71,7 +73,12 @@ func (p *GrantedProject) Process(event *models.Event) (err error) { return err } grantedProject.Name = project.Name - //TODO: read org + + org, err := p.orgEvents.OrgByID(context.TODO(), org_model.NewOrg(grantedProject.OrgID)) + if err != nil { + return err + } + p.fillOrgData(grantedProject, org) case es_model.ProjectGrantChanged: grant := new(view_model.ProjectGrant) err := grant.SetData(event) @@ -99,11 +106,12 @@ func (p *GrantedProject) Process(event *models.Event) (err error) { return p.view.PutGrantedProject(grantedProject) } -func (p *GrantedProject) getOrg(orgID string) { - //TODO: Get Org +func (p *GrantedProject) fillOrgData(grantedProject *view_model.GrantedProjectView, org *org_model.Org) { + grantedProject.OrgDomain = org.Domain + grantedProject.OrgName = org.Name } -func (p *GrantedProject) getProject(projectID string) (*model.Project, error) { +func (p *GrantedProject) getProject(projectID string) (*proj_model.Project, error) { return p.projectEvents.ProjectByID(context.Background(), projectID) } diff --git a/internal/management/repository/eventsourcing/handler/handler.go b/internal/management/repository/eventsourcing/handler/handler.go index ef6d543842..5f270ca211 100644 --- a/internal/management/repository/eventsourcing/handler/handler.go +++ b/internal/management/repository/eventsourcing/handler/handler.go @@ -1,13 +1,15 @@ package handler import ( + "time" + "github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view" + org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" - "time" ) type Configs map[string]*Config @@ -26,17 +28,20 @@ type handler struct { type EventstoreRepos struct { ProjectEvents *proj_event.ProjectEventstore UserEvents *usr_event.UserEventstore + OrgEvents *org_event.OrgEventstore } func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos) []spooler.Handler { return []spooler.Handler{ - &GrantedProject{handler: handler{view, bulkLimit, configs.cycleDuration("GrantedProject"), errorCount}, eventstore: eventstore, projectEvents: repos.ProjectEvents}, + &GrantedProject{handler: handler{view, bulkLimit, configs.cycleDuration("GrantedProject"), errorCount}, eventstore: eventstore, projectEvents: repos.ProjectEvents, orgEvents: repos.OrgEvents}, &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, projectEvents: repos.ProjectEvents}, &ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, userEvents: repos.UserEvents}, &ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount}, userEvents: repos.UserEvents}, - &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}}, - &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}}, - &UserGrant{handler: handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount}, projectEvents: repos.ProjectEvents, userEvents: repos.UserEvents}, + &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents}, + &User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, eventstore: eventstore}, + &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}, } } diff --git a/internal/management/repository/eventsourcing/handler/org.go b/internal/management/repository/eventsourcing/handler/org.go new file mode 100644 index 0000000000..cddecd83be --- /dev/null +++ b/internal/management/repository/eventsourcing/handler/org.go @@ -0,0 +1,65 @@ +package handler + +import ( + "time" + + "github.com/caos/logging" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/spooler" + org_model "github.com/caos/zitadel/internal/org/model" + "github.com/caos/zitadel/internal/org/repository/eventsourcing" + "github.com/caos/zitadel/internal/org/repository/view" +) + +type Org struct { + handler +} + +const ( + orgTable = "management.orgs" +) + +func (o *Org) MinimumCycleDuration() time.Duration { return o.cycleDuration } + +func (o *Org) ViewModel() string { + return orgTable +} + +func (o *Org) EventQuery() (*es_models.SearchQuery, error) { + sequence, err := o.view.GetLatestOrgSequence() + if err != nil { + return nil, err + } + return eventsourcing.OrgQuery(sequence), nil +} + +func (o *Org) Process(event *es_models.Event) error { + org := new(view.OrgView) + + switch event.Type { + case org_model.OrgAdded: + org.AppendEvent(event) + case org_model.OrgChanged: + err := org.SetData(event) + if err != nil { + return err + } + org, err = o.view.OrgByID(org.ID) + if err != nil { + return err + } + err = org.AppendEvent(event) + if err != nil { + return err + } + default: + return o.view.ProcessedOrgSequence(event.Sequence) + } + + return o.view.PutOrg(org) +} + +func (o *Org) OnError(event *es_models.Event, spoolerErr error) error { + logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in project app handler") + return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip) +} diff --git a/internal/management/repository/eventsourcing/handler/org_member.go b/internal/management/repository/eventsourcing/handler/org_member.go new file mode 100644 index 0000000000..79b0e0bce7 --- /dev/null +++ b/internal/management/repository/eventsourcing/handler/org_member.go @@ -0,0 +1,127 @@ +package handler + +import ( + "context" + "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" + org_model "github.com/caos/zitadel/internal/org/model" + 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" +) + +type OrgMember struct { + handler + userEvents *usr_event.UserEventstore +} + +const ( + orgMemberTable = "management.org_members" +) + +func (m *OrgMember) MinimumCycleDuration() time.Duration { return m.cycleDuration } + +func (m *OrgMember) ViewModel() string { + return orgMemberTable +} + +func (m *OrgMember) EventQuery() (*models.SearchQuery, error) { + sequence, err := m.view.GetLatestOrgMemberSequence() + if err != nil { + return nil, err + } + return es_models.NewSearchQuery(). + AggregateTypeFilter(org_model.OrgAggregate, usr_es_model.UserAggregate). + LatestSequenceFilter(sequence), nil +} + +func (m *OrgMember) Process(event *models.Event) (err error) { + switch event.AggregateType { + case org_model.OrgAggregate: + err = m.processOrgMember(event) + case usr_es_model.UserAggregate: + err = m.processUser(event) + } + return err +} + +func (m *OrgMember) processOrgMember(event *models.Event) (err error) { + member := new(view_model.OrgMemberView) + switch event.Type { + case org_model.OrgMemberAdded: + member.AppendEvent(event) + m.fillData(member) + case org_model.OrgMemberChanged: + err := member.SetData(event) + if err != nil { + return err + } + member, err = m.view.OrgMemberByIDs(event.AggregateID, member.UserID) + if err != nil { + return err + } + member.AppendEvent(event) + case org_model.OrgMemberRemoved: + err := member.SetData(event) + if err != nil { + return err + } + return m.view.DeleteOrgMember(event.AggregateID, member.UserID, event.Sequence) + default: + return m.view.ProcessedOrgMemberSequence(event.Sequence) + } + if err != nil { + return err + } + return m.view.PutOrgMember(member, member.Sequence) +} + +func (m *OrgMember) processUser(event *models.Event) (err error) { + switch event.Type { + case usr_es_model.UserProfileChanged, + usr_es_model.UserEmailChanged: + members, err := m.view.OrgMembersByUserID(event.AggregateID) + if err != nil { + return err + } + user, err := m.userEvents.UserByID(context.Background(), event.AggregateID) + if err != nil { + return err + } + for _, member := range members { + m.fillUserData(member, user) + err = m.view.PutOrgMember(member, event.Sequence) + if err != nil { + return err + } + } + default: + return m.view.ProcessedOrgMemberSequence(event.Sequence) + } + return nil +} + +func (m *OrgMember) fillData(member *view_model.OrgMemberView) (err error) { + user, err := m.userEvents.UserByID(context.Background(), member.UserID) + if err != nil { + return err + } + m.fillUserData(member, user) + return nil +} + +func (m *OrgMember) fillUserData(member *view_model.OrgMemberView, user *usr_model.User) { + member.UserName = user.UserName + member.FirstName = user.FirstName + member.LastName = user.LastName + member.Email = user.EmailAddress +} +func (m *OrgMember) OnError(event *models.Event, err error) error { + logging.LogWithFields("SPOOL-u73es", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgmember handler") + return spooler.HandleError(event, err, m.view.GetLatestOrgMemberFailedEvent, m.view.ProcessedOrgMemberFailedEvent, m.view.ProcessedOrgMemberSequence, m.errorCountUntilSkip) +} diff --git a/internal/management/repository/eventsourcing/handler/user_grant.go b/internal/management/repository/eventsourcing/handler/user_grant.go index abbd4c135f..661b9cfbcb 100644 --- a/internal/management/repository/eventsourcing/handler/user_grant.go +++ b/internal/management/repository/eventsourcing/handler/user_grant.go @@ -2,7 +2,11 @@ package handler import ( "context" + "time" + 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" proj_model "github.com/caos/zitadel/internal/project/model" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" @@ -10,7 +14,6 @@ import ( usr_events "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" - "time" "github.com/caos/logging" @@ -25,6 +28,7 @@ type UserGrant struct { eventstore eventstore.Eventstore projectEvents *proj_event.ProjectEventstore userEvents *usr_events.UserEventstore + orgEvents *org_events.OrgEventstore } const ( @@ -67,7 +71,7 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) { if err != nil { return err } - err = u.fillData(grant) + err = u.fillData(grant, event.ResourceOwner) case grant_es_model.UserGrantChanged, grant_es_model.UserGrantDeactivated, grant_es_model.UserGrantReactivated: @@ -133,7 +137,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) { return nil } -func (u *UserGrant) fillData(grant *view_model.UserGrantView) (err error) { +func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner string) (err error) { user, err := u.userEvents.UserByID(context.Background(), grant.UserID) if err != nil { return err @@ -144,7 +148,12 @@ func (u *UserGrant) fillData(grant *view_model.UserGrantView) (err error) { return err } u.fillProjectData(grant, project) - u.fillOrgData(grant) + + org, err := u.orgEvents.OrgByID(context.TODO(), org_model.NewOrg(resourceOwner)) + if err != nil { + return err + } + u.fillOrgData(grant, org) return nil } @@ -159,8 +168,9 @@ func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *pr grant.ProjectName = project.Name } -func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView) { - //TODO: get ORG +func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) { + grant.OrgDomain = org.Domain + grant.OrgName = org.Name } func (u *UserGrant) OnError(event *models.Event, err error) error { diff --git a/internal/management/repository/eventsourcing/org.go b/internal/management/repository/eventsourcing/org.go deleted file mode 100644 index ee13648b25..0000000000 --- a/internal/management/repository/eventsourcing/org.go +++ /dev/null @@ -1,52 +0,0 @@ -package eventsourcing - -import ( - "context" - - "github.com/caos/zitadel/internal/errors" - org_model "github.com/caos/zitadel/internal/org/model" - org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" -) - -type OrgRepository struct { - *org_es.OrgEventstore -} - -func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.Org, error) { - org := org_model.NewOrg(id) - return repo.OrgEventstore.OrgByID(ctx, org) -} - -func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.Org, error) { - return nil, errors.ThrowUnimplemented(nil, "EVENT-GQoS8", "not implemented") -} - -func (repo *OrgRepository) UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error) { - return nil, errors.ThrowUnimplemented(nil, "EVENT-RkurR", "not implemented") -} - -func (repo *OrgRepository) DeactivateOrg(ctx context.Context, id string) (*org_model.Org, error) { - return repo.OrgEventstore.DeactivateOrg(ctx, id) -} - -func (repo *OrgRepository) ReactivateOrg(ctx context.Context, id string) (*org_model.Org, error) { - return repo.OrgEventstore.ReactivateOrg(ctx, id) -} - -func (repo *OrgRepository) OrgMemberByID(ctx context.Context, orgID, userID string) (member *org_model.OrgMember, err error) { - member = org_model.NewOrgMember(orgID, userID) - return repo.OrgEventstore.OrgMemberByIDs(ctx, member) -} - -func (repo *OrgRepository) AddOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) { - return repo.OrgEventstore.AddOrgMember(ctx, member) -} - -func (repo *OrgRepository) ChangeOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) { - return repo.OrgEventstore.ChangeOrgMember(ctx, member) -} - -func (repo *OrgRepository) RemoveOrgMember(ctx context.Context, orgID, userID string) error { - member := org_model.NewOrgMember(orgID, userID) - return repo.OrgEventstore.RemoveOrgMember(ctx, member) -} diff --git a/internal/management/repository/eventsourcing/repository.go b/internal/management/repository/eventsourcing/repository.go index 9dfd3cbd02..ca793768a4 100644 --- a/internal/management/repository/eventsourcing/repository.go +++ b/internal/management/repository/eventsourcing/repository.go @@ -34,7 +34,7 @@ type EsRepository struct { eventstore.PolicyRepo } -func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) { +func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRepository, error) { es, err := es_int.Start(conf.Eventstore) if err != nil { return nil, err @@ -79,13 +79,13 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) } org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es}) - eventstoreRepos := handler.EventstoreRepos{ProjectEvents: project, UserEvents: user} + eventstoreRepos := handler.EventstoreRepos{ProjectEvents: project, UserEvents: user, OrgEvents: org} spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos) return &EsRepository{ spooler: spool, - OrgRepository: eventstore.OrgRepository{org}, - ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view}, + OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles}, + ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles}, UserRepo: eventstore.UserRepo{conf.SearchLimit, user, view}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view}, PolicyRepo: eventstore.PolicyRepo{policy}, diff --git a/internal/management/repository/eventsourcing/view/org.go b/internal/management/repository/eventsourcing/view/org.go new file mode 100644 index 0000000000..445a17a496 --- /dev/null +++ b/internal/management/repository/eventsourcing/view/org.go @@ -0,0 +1,42 @@ +package view + +import ( + org_view "github.com/caos/zitadel/internal/org/repository/view" + "github.com/caos/zitadel/internal/view" +) + +const ( + orgTable = "management.orgs" +) + +func (v *View) OrgByID(orgID string) (*org_view.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 { + err := org_view.PutOrg(v.Db, orgTable, org) + if err != nil { + return err + } + return v.ProcessedOrgSequence(org.Sequence) +} + +func (v *View) GetLatestOrgFailedEvent(sequence uint64) (*view.FailedEvent, error) { + return v.latestFailedEvent(orgTable, sequence) +} + +func (v *View) ProcessedOrgFailedEvent(failedEvent *view.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} + +func (v *View) GetLatestOrgSequence() (uint64, error) { + return v.latestSequence(orgTable) +} + +func (v *View) ProcessedOrgSequence(eventSequence uint64) error { + return v.saveCurrentSequence(orgTable, eventSequence) +} diff --git a/internal/management/repository/eventsourcing/view/org_member.go b/internal/management/repository/eventsourcing/view/org_member.go new file mode 100644 index 0000000000..c1a77085e0 --- /dev/null +++ b/internal/management/repository/eventsourcing/view/org_member.go @@ -0,0 +1,55 @@ +package view + +import ( + org_model "github.com/caos/zitadel/internal/org/model" + "github.com/caos/zitadel/internal/org/repository/view" + global_view "github.com/caos/zitadel/internal/view" +) + +const ( + orgMemberTable = "management.org_members" +) + +func (v *View) OrgMemberByIDs(orgID, userID string) (*view.OrgMemberView, error) { + return view.OrgMemberByIDs(v.Db, orgMemberTable, orgID, userID) +} + +func (v *View) SearchOrgMembers(request *org_model.OrgMemberSearchRequest) ([]*view.OrgMemberView, int, error) { + return view.SearchOrgMembers(v.Db, orgMemberTable, request) +} + +func (v *View) OrgMembersByUserID(userID string) ([]*view.OrgMemberView, error) { + return view.OrgMembersByUserID(v.Db, orgMemberTable, userID) +} + +func (v *View) PutOrgMember(org *view.OrgMemberView, sequence uint64) error { + err := view.PutOrgMember(v.Db, orgMemberTable, org) + if err != nil { + return err + } + return v.ProcessedOrgMemberSequence(sequence) +} + +func (v *View) DeleteOrgMember(orgID, userID string, eventSequence uint64) error { + err := view.DeleteOrgMember(v.Db, orgMemberTable, orgID, userID) + if err != nil { + return nil + } + return v.ProcessedOrgMemberSequence(eventSequence) +} + +func (v *View) GetLatestOrgMemberSequence() (uint64, error) { + return v.latestSequence(orgMemberTable) +} + +func (v *View) ProcessedOrgMemberSequence(eventSequence uint64) error { + return v.saveCurrentSequence(orgMemberTable, eventSequence) +} + +func (v *View) GetLatestOrgMemberFailedEvent(sequence uint64) (*global_view.FailedEvent, error) { + return v.latestFailedEvent(orgMemberTable, sequence) +} + +func (v *View) ProcessedOrgMemberFailedEvent(failedEvent *global_view.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} diff --git a/internal/management/repository/org.go b/internal/management/repository/org.go index 190496e7e1..a32934a485 100644 --- a/internal/management/repository/org.go +++ b/internal/management/repository/org.go @@ -8,14 +8,15 @@ import ( type OrgRepository interface { OrgByID(ctx context.Context, id string) (*org_model.Org, error) - OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.Org, error) + OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, 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) -} -type OrgMemberRepository interface { + SearchOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error) AddOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) ChangeOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) RemoveOrgMember(ctx context.Context, orgID, userID string) error + + GetOrgMemberRoles() []string } diff --git a/internal/management/repository/project.go b/internal/management/repository/project.go index 38fe17d668..bac0aa40f1 100644 --- a/internal/management/repository/project.go +++ b/internal/management/repository/project.go @@ -2,6 +2,7 @@ package repository import ( "context" + "github.com/caos/zitadel/internal/project/model" ) @@ -18,6 +19,7 @@ type ProjectRepository interface { ChangeProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error) RemoveProjectMember(ctx context.Context, projectID, userID string) error SearchProjectMembers(ctx context.Context, request *model.ProjectMemberSearchRequest) (*model.ProjectMemberSearchResponse, error) + GetProjectMemberRoles() []string AddProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error) ChangeProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error) @@ -46,4 +48,5 @@ type ProjectRepository interface { AddProjectGrantMember(ctx context.Context, member *model.ProjectGrantMember) (*model.ProjectGrantMember, error) ChangeProjectGrantMember(ctx context.Context, member *model.ProjectGrantMember) (*model.ProjectGrantMember, error) RemoveProjectGrantMember(ctx context.Context, projectID, grantID, userID string) error + GetProjectGrantMemberRoles() []string } diff --git a/internal/management/repository/repository.go b/internal/management/repository/repository.go index 3a00e8d4d2..ee346cffb1 100644 --- a/internal/management/repository/repository.go +++ b/internal/management/repository/repository.go @@ -5,7 +5,6 @@ type Repository interface { ProjectRepository PolicyRepository OrgRepository - OrgMemberRepository UserRepository UserGrantRepository } diff --git a/internal/org/model/org_member_view.go b/internal/org/model/org_member_view.go new file mode 100644 index 0000000000..b3d972ec1b --- /dev/null +++ b/internal/org/model/org_member_view.go @@ -0,0 +1,59 @@ +package model + +import ( + "time" + + "github.com/caos/zitadel/internal/model" +) + +type OrgMemberView struct { + UserID string + OrgID string + UserName string + Email string + FirstName string + LastName string + Roles []string + CreationDate time.Time + ChangeDate time.Time + Sequence uint64 +} + +type OrgMemberSearchRequest struct { + Offset uint64 + Limit uint64 + SortingColumn OrgMemberSearchKey + Asc bool + Queries []*OrgMemberSearchQuery +} + +type OrgMemberSearchKey int32 + +const ( + ORGMEMBERSEARCHKEY_UNSPECIFIED OrgMemberSearchKey = iota + ORGMEMBERSEARCHKEY_USER_NAME + ORGMEMBERSEARCHKEY_EMAIL + ORGMEMBERSEARCHKEY_FIRST_NAME + ORGMEMBERSEARCHKEY_LAST_NAME + ORGMEMBERSEARCHKEY_ORG_ID + ORGMEMBERSEARCHKEY_USER_ID +) + +type OrgMemberSearchQuery struct { + Key OrgMemberSearchKey + Method model.SearchMethod + Value string +} + +type OrgMemberSearchResponse struct { + Offset uint64 + Limit uint64 + TotalResult uint64 + Result []*OrgMemberView +} + +func (r *OrgMemberSearchRequest) EnsureLimit(limit uint64) { + if r.Limit == 0 || r.Limit > limit { + r.Limit = limit + } +} diff --git a/internal/org/model/org_view.go b/internal/org/model/org_view.go new file mode 100644 index 0000000000..86f05dee84 --- /dev/null +++ b/internal/org/model/org_view.go @@ -0,0 +1,73 @@ +package model + +import ( + "time" + + "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/model" +) + +type OrgView struct { + ID string + CreationDate time.Time + ChangeDate time.Time + State OrgState + ResourceOwner string + Sequence uint64 + + Name string + Domain string +} + +type OrgSearchRequest struct { + Offset uint64 + Limit uint64 + SortingColumn OrgSearchKey + Asc bool + Queries []*OrgSearchQuery +} + +type OrgSearchKey int32 + +const ( + ORGSEARCHKEY_UNSPECIFIED OrgSearchKey = iota + ORGSEARCHKEY_ORG_ID + ORGSEARCHKEY_ORG_NAME + ORGSEARCHKEY_ORG_DOMAIN + ORGSEARCHKEY_STATE + ORGSEARCHKEY_RESOURCEOWNER +) + +type OrgSearchQuery struct { + Key OrgSearchKey + Method model.SearchMethod + Value string +} + +type OrgSearchResult struct { + Offset uint64 + Limit uint64 + TotalResult uint64 + Result []*OrgView +} + +func (r *OrgSearchRequest) EnsureLimit(limit uint64) { + if r.Limit == 0 || r.Limit > limit { + r.Limit = limit + } +} + +func OrgViewToOrg(o *OrgView) *Org { + return &Org{ + ObjectRoot: models.ObjectRoot{ + AggregateID: o.ID, + ChangeDate: o.ChangeDate, + CreationDate: o.CreationDate, + ResourceOwner: o.ResourceOwner, + Sequence: o.Sequence, + }, + Domain: o.Domain, + Name: o.Name, + State: o.State, + } +} diff --git a/internal/org/repository/view/org.go b/internal/org/repository/view/org.go new file mode 100644 index 0000000000..b6aee2cf4a --- /dev/null +++ b/internal/org/repository/view/org.go @@ -0,0 +1,101 @@ +package view + +import ( + "encoding/json" + "time" + + "github.com/caos/logging" + "github.com/caos/zitadel/internal/errors" + es_models "github.com/caos/zitadel/internal/eventstore/models" + org_es_model "github.com/caos/zitadel/internal/org/model" + org_model "github.com/caos/zitadel/internal/org/model" +) + +const ( + OrgKeyOrgDomain = "domain" + OrgKeyOrgID = "id" + OrgKeyOrgName = "name" + OrgKeyResourceOwner = "resource_owner" + OrgKeyState = "org_state" +) + +type OrgView 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:org_state"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + + Name string `json:"name" gorm:"column:name"` + Domain string `json:"domain" gorm:"column:domain"` +} + +func OrgFromModel(org *org_model.OrgView) *OrgView { + return &OrgView{ + Domain: org.Domain, + ChangeDate: org.ChangeDate, + CreationDate: org.CreationDate, + ID: org.ID, + Name: org.Name, + ResourceOwner: org.ResourceOwner, + Sequence: org.Sequence, + State: int32(org.State), + } +} + +func OrgToModel(org *OrgView) *org_model.OrgView { + return &org_model.OrgView{ + Domain: org.Domain, + ChangeDate: org.ChangeDate, + CreationDate: org.CreationDate, + ID: org.ID, + Name: org.Name, + ResourceOwner: org.ResourceOwner, + Sequence: org.Sequence, + State: org_model.OrgState(org.State), + } +} + +func OrgsToModel(orgs []*OrgView) []*org_model.OrgView { + modelOrgs := make([]*org_model.OrgView, len(orgs)) + + for i, org := range orgs { + modelOrgs[i] = OrgToModel(org) + } + + return modelOrgs +} + +func (o *OrgView) AppendEvent(event *es_models.Event) (err error) { + switch event.Type { + case org_es_model.OrgAdded: + o.CreationDate = event.CreationDate + o.State = int32(org_model.ORGSTATE_ACTIVE) + o.setRootData(event) + err = o.SetData(event) + case org_es_model.OrgChanged: + o.setRootData(event) + err = o.SetData(event) + case org_es_model.OrgDeactivated: + o.State = int32(org_model.ORGSTATE_INACTIVE) + case org_es_model.OrgReactivated: + o.State = int32(org_model.ORGSTATE_ACTIVE) + } + return err +} + +func (o *OrgView) setRootData(event *es_models.Event) { + o.ChangeDate = event.CreationDate + o.Sequence = event.Sequence + o.ID = event.AggregateID + o.ResourceOwner = event.ResourceOwner +} + +func (o *OrgView) SetData(event *es_models.Event) error { + if err := json.Unmarshal(event.Data, o); err != nil { + logging.Log("VIEW-5W7Op").WithError(err).Error("could not unmarshal event data") + return errors.ThrowInternal(err, "VIEW-HZKME", "Could not unmarshal data") + } + return nil +} diff --git a/internal/org/repository/view/org_member.go b/internal/org/repository/view/org_member.go new file mode 100644 index 0000000000..b4ebddcd98 --- /dev/null +++ b/internal/org/repository/view/org_member.go @@ -0,0 +1,99 @@ +package view + +import ( + "encoding/json" + "time" + + "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" + "github.com/lib/pq" +) + +const ( + OrgMemberKeyUserID = "user_id" + OrgMemberKeyOrgID = "org_id" + OrgMemberKeyUserName = "user_name" + OrgMemberKeyEmail = "email" + OrgMemberKeyFirstName = "first_name" + OrgMemberKeyLastName = "last_name" +) + +type OrgMemberView struct { + UserID string `json:"userId" gorm:"column:user_id;primary_key"` + OrgID string `json:"-" gorm:"column:org_id;primary_key"` + UserName string `json:"-" gorm:"column:user_name"` + Email string `json:"-" gorm:"column:email_address"` + FirstName string `json:"-" gorm:"column:first_name"` + LastName string `json:"-" gorm:"column:last_name"` + Roles pq.StringArray `json:"roles" gorm:"column:roles"` + Sequence uint64 `json:"-" gorm:"column:sequence"` + + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` +} + +func OrgMemberViewFromModel(member *model.OrgMemberView) *OrgMemberView { + return &OrgMemberView{ + UserID: member.UserID, + OrgID: member.OrgID, + UserName: member.UserName, + Email: member.Email, + FirstName: member.FirstName, + LastName: member.LastName, + Roles: member.Roles, + Sequence: member.Sequence, + CreationDate: member.CreationDate, + ChangeDate: member.ChangeDate, + } +} + +func OrgMemberToModel(member *OrgMemberView) *model.OrgMemberView { + return &model.OrgMemberView{ + UserID: member.UserID, + OrgID: member.OrgID, + UserName: member.UserName, + Email: member.Email, + FirstName: member.FirstName, + LastName: member.LastName, + Roles: member.Roles, + Sequence: member.Sequence, + CreationDate: member.CreationDate, + ChangeDate: member.ChangeDate, + } +} + +func OrgMembersToModel(roles []*OrgMemberView) []*model.OrgMemberView { + result := make([]*model.OrgMemberView, len(roles)) + for i, r := range roles { + result[i] = OrgMemberToModel(r) + } + return result +} + +func (r *OrgMemberView) AppendEvent(event *models.Event) (err error) { + r.Sequence = event.Sequence + r.ChangeDate = event.CreationDate + switch event.Type { + case model.OrgMemberAdded: + r.setRootData(event) + r.CreationDate = event.CreationDate + err = r.SetData(event) + case model.OrgMemberChanged: + err = r.SetData(event) + } + return err +} + +func (r *OrgMemberView) setRootData(event *models.Event) { + r.OrgID = event.AggregateID +} + +func (r *OrgMemberView) SetData(event *models.Event) error { + if err := json.Unmarshal(event.Data, r); err != nil { + logging.Log("EVEN-slo9s").WithError(err).Error("could not unmarshal event data") + return caos_errs.ThrowInternal(err, "MODEL-lub6s", "Could not unmarshal data") + } + return nil +} diff --git a/internal/org/repository/view/org_member_query.go b/internal/org/repository/view/org_member_query.go new file mode 100644 index 0000000000..e599967039 --- /dev/null +++ b/internal/org/repository/view/org_member_query.go @@ -0,0 +1,69 @@ +package view + +import ( + global_model "github.com/caos/zitadel/internal/model" + proj_model "github.com/caos/zitadel/internal/org/model" + "github.com/caos/zitadel/internal/view" +) + +type OrgMemberSearchRequest proj_model.OrgMemberSearchRequest +type OrgMemberSearchQuery proj_model.OrgMemberSearchQuery +type OrgMemberSearchKey proj_model.OrgMemberSearchKey + +func (req OrgMemberSearchRequest) GetLimit() uint64 { + return req.Limit +} + +func (req OrgMemberSearchRequest) GetOffset() uint64 { + return req.Offset +} + +func (req OrgMemberSearchRequest) GetSortingColumn() view.ColumnKey { + if req.SortingColumn == proj_model.ORGMEMBERSEARCHKEY_UNSPECIFIED { + return nil + } + return OrgMemberSearchKey(req.SortingColumn) +} + +func (req OrgMemberSearchRequest) GetAsc() bool { + return req.Asc +} + +func (req OrgMemberSearchRequest) GetQueries() []view.SearchQuery { + result := make([]view.SearchQuery, len(req.Queries)) + for i, q := range req.Queries { + result[i] = OrgMemberSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} + } + return result +} + +func (req OrgMemberSearchQuery) GetKey() view.ColumnKey { + return OrgMemberSearchKey(req.Key) +} + +func (req OrgMemberSearchQuery) GetMethod() global_model.SearchMethod { + return req.Method +} + +func (req OrgMemberSearchQuery) GetValue() interface{} { + return req.Value +} + +func (key OrgMemberSearchKey) ToColumnName() string { + switch proj_model.OrgMemberSearchKey(key) { + case proj_model.ORGMEMBERSEARCHKEY_EMAIL: + return OrgMemberKeyEmail + case proj_model.ORGMEMBERSEARCHKEY_FIRST_NAME: + return OrgMemberKeyFirstName + case proj_model.ORGMEMBERSEARCHKEY_LAST_NAME: + return OrgMemberKeyLastName + case proj_model.ORGMEMBERSEARCHKEY_USER_NAME: + return OrgMemberKeyUserName + case proj_model.ORGMEMBERSEARCHKEY_USER_ID: + return OrgMemberKeyUserID + case proj_model.ORGMEMBERSEARCHKEY_ORG_ID: + return OrgMemberKeyOrgID + default: + return "" + } +} diff --git a/internal/org/repository/view/org_member_view.go b/internal/org/repository/view/org_member_view.go new file mode 100644 index 0000000000..fbaa74cc0e --- /dev/null +++ b/internal/org/repository/view/org_member_view.go @@ -0,0 +1,58 @@ +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/view" + "github.com/jinzhu/gorm" +) + +func OrgMemberByIDs(db *gorm.DB, table, orgID, userID string) (*OrgMemberView, error) { + member := new(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} + 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}) + 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) + queries := []*org_model.OrgMemberSearchQuery{ + { + Key: org_model.ORGMEMBERSEARCHKEY_USER_ID, + Value: userID, + Method: global_model.SEARCHMETHOD_EQUALS, + }, + } + query := view.PrepareSearchQuery(table, OrgMemberSearchRequest{Queries: queries}) + _, err := query(db, &members) + if err != nil { + return nil, err + } + return members, nil +} + +func PutOrgMember(db *gorm.DB, table string, role *OrgMemberView) error { + save := view.PrepareSave(table) + return save(db, role) +} + +func DeleteOrgMember(db *gorm.DB, table, orgID, userID string) error { + member, err := OrgMemberByIDs(db, table, orgID, userID) + if err != nil { + return err + } + delete := view.PrepareDeleteByObject(table, member) + return delete(db) +} diff --git a/internal/org/repository/view/org_query.go b/internal/org/repository/view/org_query.go new file mode 100644 index 0000000000..f5a83d8941 --- /dev/null +++ b/internal/org/repository/view/org_query.go @@ -0,0 +1,67 @@ +package view + +import ( + global_model "github.com/caos/zitadel/internal/model" + usr_model "github.com/caos/zitadel/internal/org/model" + "github.com/caos/zitadel/internal/view" +) + +type OrgSearchRequest usr_model.OrgSearchRequest +type OrgSearchQuery usr_model.OrgSearchQuery +type OrgSearchKey usr_model.OrgSearchKey + +func (req OrgSearchRequest) GetLimit() uint64 { + return req.Limit +} + +func (req OrgSearchRequest) GetOffset() uint64 { + return req.Offset +} + +func (req OrgSearchRequest) GetSortingColumn() view.ColumnKey { + if req.SortingColumn == usr_model.ORGSEARCHKEY_UNSPECIFIED { + return nil + } + return OrgSearchKey(req.SortingColumn) +} + +func (req OrgSearchRequest) GetAsc() bool { + return req.Asc +} + +func (req OrgSearchRequest) GetQueries() []view.SearchQuery { + result := make([]view.SearchQuery, len(req.Queries)) + for i, q := range req.Queries { + result[i] = OrgSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} + } + return result +} + +func (req OrgSearchQuery) GetKey() view.ColumnKey { + return OrgSearchKey(req.Key) +} + +func (req OrgSearchQuery) GetMethod() global_model.SearchMethod { + return req.Method +} + +func (req OrgSearchQuery) GetValue() interface{} { + return req.Value +} + +func (key OrgSearchKey) ToColumnName() string { + switch usr_model.OrgSearchKey(key) { + case usr_model.ORGSEARCHKEY_ORG_DOMAIN: + return OrgKeyOrgDomain + case usr_model.ORGSEARCHKEY_ORG_ID: + return OrgKeyOrgID + case usr_model.ORGSEARCHKEY_ORG_NAME: + return OrgKeyOrgName + case usr_model.ORGSEARCHKEY_RESOURCEOWNER: + return OrgKeyResourceOwner + case usr_model.ORGSEARCHKEY_STATE: + return OrgKeyState + default: + return "" + } +} diff --git a/internal/org/repository/view/org_view.go b/internal/org/repository/view/org_view.go new file mode 100644 index 0000000000..bfd1b89858 --- /dev/null +++ b/internal/org/repository/view/org_view.go @@ -0,0 +1,41 @@ +package view + +import ( + org_model "github.com/caos/zitadel/internal/org/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) + 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}) + count, err := query(db, &orgs) + if err != nil { + return nil, 0, err + } + 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 { + 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) + return delete(db) +} diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go index 01a135d011..7545724149 100644 --- a/internal/user/repository/eventsourcing/eventstore.go +++ b/internal/user/repository/eventsourcing/eventstore.go @@ -55,15 +55,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto) aesOtpCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey) passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost) - if err != nil { - return nil, err - } - mfa := global_model.Multifactors{ - OTP: global_model.OTP{ - CryptoMFA: aesOtpCrypto, - Issuer: systemDefaults.Multifactors.OTP.Issuer, - }, - } + return &UserEventstore{ Eventstore: conf.Eventstore, userCache: userCache, @@ -72,9 +64,14 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto EmailVerificationCode: emailVerificationCode, PhoneVerificationCode: phoneVerificationCode, PasswordVerificationCode: passwordVerificationCode, - Multifactors: mfa, - PasswordAlg: passwordAlg, - validateTOTP: totp.Validate, + Multifactors: global_model.Multifactors{ + OTP: global_model.OTP{ + CryptoMFA: aesOtpCrypto, + Issuer: systemDefaults.Multifactors.OTP.Issuer, + }, + }, + PasswordAlg: passwordAlg, + validateTOTP: totp.Validate, }, nil } diff --git a/internal/usergrant/repository/view/model/user_grant.go b/internal/usergrant/repository/view/model/user_grant.go index 0dbc021f10..3ea8518755 100644 --- a/internal/usergrant/repository/view/model/user_grant.go +++ b/internal/usergrant/repository/view/model/user_grant.go @@ -2,13 +2,14 @@ package model import ( "encoding/json" + "time" + "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/usergrant/model" es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" "github.com/lib/pq" - "time" ) const ( diff --git a/migrations/cockroach/V1.10__mgmt_orgs.sql b/migrations/cockroach/V1.10__mgmt_orgs.sql new file mode 100644 index 0000000000..46f0d4627f --- /dev/null +++ b/migrations/cockroach/V1.10__mgmt_orgs.sql @@ -0,0 +1,34 @@ +BEGIN; + +CREATE TABLE management.orgs ( + id TEXT, + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + resource_owner TEXT, + org_state SMALLINT, + sequence BIGINT, + + domain TEXT, + name TEXT, + + PRIMARY KEY (id) +); + +CREATE TABLE management.org_members ( + user_id TEXT, + org_id TEXT, + + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + + user_name TEXT, + email_address TEXT, + first_name TEXT, + last_name TEXT, + roles TEXT ARRAY, + sequence BIGINT, + + PRIMARY KEY (org_id, user_id) +); + +COMMIT; diff --git a/migrations/cockroach/V1.8__admin.sql b/migrations/cockroach/V1.8__admin.sql new file mode 100644 index 0000000000..5ec450b3d1 --- /dev/null +++ b/migrations/cockroach/V1.8__admin.sql @@ -0,0 +1,49 @@ +BEGIN; + +CREATE DATABASE admin_api; + +COMMIT; + +BEGIN; + +CREATE TABLE admin_api.orgs ( + id TEXT, + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + resource_owner TEXT, + org_state SMALLINT, + sequence BIGINT, + + domain TEXT, + name TEXT, + + PRIMARY KEY (id) +); + +CREATE TABLE admin_api.failed_event ( + view_name TEXT, + failed_sequence BIGINT, + failure_count SMALLINT, + err_msg TEXT, + + PRIMARY KEY (view_name, failed_sequence) +); + +CREATE TABLE admin_api.locks ( + locker_id TEXT, + locked_until TIMESTAMPTZ, + object_type TEXT, + + PRIMARY KEY (object_type) +); + +CREATE TABLE admin_api.current_sequences ( + view_name TEXT, + + current_sequence BIGINT, + + PRIMARY KEY (view_name) +); + + +COMMIT; diff --git a/migrations/cockroach/V1.9__admin_grant.sql b/migrations/cockroach/V1.9__admin_grant.sql new file mode 100644 index 0000000000..c6e53ce7ff --- /dev/null +++ b/migrations/cockroach/V1.9__admin_grant.sql @@ -0,0 +1,6 @@ +BEGIN; + +GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE admin_api TO admin_api; +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE admin_api.* TO admin_api; + +COMMIT; diff --git a/pkg/admin/api/grpc/org.go b/pkg/admin/api/grpc/org.go index 8a389f27c0..6b3bfa0264 100644 --- a/pkg/admin/api/grpc/org.go +++ b/pkg/admin/api/grpc/org.go @@ -2,6 +2,10 @@ package grpc import ( "context" + + "github.com/caos/zitadel/internal/model" + + org_model "github.com/caos/zitadel/internal/org/model" ) func (s *Server) GetOrgByID(ctx context.Context, orgID *OrgID) (_ *Org, err error) { @@ -13,14 +17,15 @@ func (s *Server) GetOrgByID(ctx context.Context, orgID *OrgID) (_ *Org, err erro } func (s *Server) SearchOrgs(ctx context.Context, request *OrgSearchRequest) (_ *OrgSearchResponse, err error) { - orgs, err := s.org.SearchOrgs(ctx) + result, err := s.org.SearchOrgs(ctx, orgSearchRequestToModel(request)) if err != nil { return nil, err } - return &OrgSearchResponse{Result: orgsFromModel(orgs), - Limit: request.Limit, - Offset: request.Offset, - // TotalResult: , TODO: total result from search + return &OrgSearchResponse{ + Result: orgViewsFromModel(result.Result), + Limit: request.Limit, + Offset: request.Offset, + TotalResult: result.TotalResult, }, nil } @@ -37,3 +42,57 @@ 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 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 + } +} diff --git a/pkg/admin/api/grpc/org_converter.go b/pkg/admin/api/grpc/org_converter.go index 680d786163..a3d0d37165 100644 --- a/pkg/admin/api/grpc/org_converter.go +++ b/pkg/admin/api/grpc/org_converter.go @@ -65,10 +65,10 @@ func setUpOrgResponseFromModel(setUp *admin_model.SetupOrg) *OrgSetUpResponse { } } -func orgsFromModel(orgs []*org_model.Org) []*Org { +func orgViewsFromModel(orgs []*org_model.OrgView) []*Org { result := make([]*Org, len(orgs)) for i, org := range orgs { - result[i] = orgFromModel(org) + result[i] = orgViewFromModel(org) } return result @@ -91,6 +91,23 @@ func orgFromModel(org *org_model.Org) *Org { } } +func orgViewFromModel(org *org_model.OrgView) *Org { + creationDate, err := ptypes.TimestampProto(org.CreationDate) + logging.Log("GRPC-GTHsZ").OnError(err).Debug("unable to get timestamp from time") + + changeDate, err := ptypes.TimestampProto(org.ChangeDate) + 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, + Name: org.Name, + State: orgStateFromModel(org.State), + } +} + func userFromModel(user *usr_model.User) *User { creationDate, err := ptypes.TimestampProto(user.CreationDate) logging.Log("GRPC-8duwe").OnError(err).Debug("unable to parse timestamp") diff --git a/pkg/management/api/grpc/org.go b/pkg/management/api/grpc/org.go index 7254bfccc0..c8bace9c73 100644 --- a/pkg/management/api/grpc/org.go +++ b/pkg/management/api/grpc/org.go @@ -19,7 +19,7 @@ func (s *Server) GetOrgByDomainGlobal(ctx context.Context, in *OrgDomain) (*Org, if err != nil { return nil, err } - return orgFromModel(org), nil + return orgFromView(org), nil } func (s *Server) DeactivateOrg(ctx context.Context, in *OrgID) (*Org, error) { diff --git a/pkg/management/api/grpc/org_converter.go b/pkg/management/api/grpc/org_converter.go index d79dcae7b6..7003466326 100644 --- a/pkg/management/api/grpc/org_converter.go +++ b/pkg/management/api/grpc/org_converter.go @@ -31,6 +31,23 @@ func orgFromModel(org *org_model.Org) *Org { } } +func orgFromView(org *org_model.OrgView) *Org { + creationDate, err := ptypes.TimestampProto(org.CreationDate) + logging.Log("GRPC-GTHsZ").OnError(err).Debug("unable to get timestamp from time") + + changeDate, err := ptypes.TimestampProto(org.ChangeDate) + 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, + Name: org.Name, + State: orgStateFromModel(org.State), + } +} + func orgStateFromModel(state org_model.OrgState) OrgState { switch state { case org_model.ORGSTATE_ACTIVE: diff --git a/pkg/management/api/grpc/org_member.go b/pkg/management/api/grpc/org_member.go index e493818763..62c3a8ac92 100644 --- a/pkg/management/api/grpc/org_member.go +++ b/pkg/management/api/grpc/org_member.go @@ -3,22 +3,25 @@ package grpc import ( "context" - "github.com/caos/zitadel/internal/errors" "github.com/golang/protobuf/ptypes/empty" ) func (s *Server) GetOrgMemberRoles(ctx context.Context, _ *empty.Empty) (*OrgMemberRoles, error) { - return nil, errors.ThrowUnimplemented(nil, "GRPC-wz4vc", "Not implemented") + return &OrgMemberRoles{Roles: s.org.GetOrgMemberRoles()}, nil } func (s *Server) SearchOrgMembers(ctx context.Context, in *OrgMemberSearchRequest) (*OrgMemberSearchResponse, error) { - return nil, errors.ThrowUnimplemented(nil, "GRPC-wkdl3", "Not implemented") + members, err := s.org.SearchOrgMembers(ctx, orgMemberSearchRequestToModel(in)) + if err != nil { + return nil, err + } + return orgMemberSearchResponseFromModel(members), nil } func (s *Server) AddOrgMember(ctx context.Context, member *AddOrgMemberRequest) (*OrgMember, error) { repositoryMember := addOrgMemberToModel(member) - addedMember, err := s.orgMember.AddOrgMember(ctx, repositoryMember) + addedMember, err := s.org.AddOrgMember(ctx, repositoryMember) if err != nil { return nil, err } @@ -28,7 +31,7 @@ func (s *Server) AddOrgMember(ctx context.Context, member *AddOrgMemberRequest) func (s *Server) ChangeOrgMember(ctx context.Context, member *ChangeOrgMemberRequest) (*OrgMember, error) { repositoryMember := changeOrgMemberToModel(member) - changedMember, err := s.orgMember.ChangeOrgMember(ctx, repositoryMember) + changedMember, err := s.org.ChangeOrgMember(ctx, repositoryMember) if err != nil { return nil, err } @@ -36,6 +39,6 @@ func (s *Server) ChangeOrgMember(ctx context.Context, member *ChangeOrgMemberReq } func (s *Server) RemoveOrgMember(ctx context.Context, member *RemoveOrgMemberRequest) (*empty.Empty, error) { - err := s.orgMember.RemoveOrgMember(ctx, member.OrgId, member.UserId) + err := s.org.RemoveOrgMember(ctx, member.OrgId, member.UserId) return &empty.Empty{}, err } diff --git a/pkg/management/api/grpc/org_member_converter.go b/pkg/management/api/grpc/org_member_converter.go index 7f27b41983..cda63958c6 100644 --- a/pkg/management/api/grpc/org_member_converter.go +++ b/pkg/management/api/grpc/org_member_converter.go @@ -2,6 +2,7 @@ package grpc import ( "github.com/caos/logging" + "github.com/caos/zitadel/internal/model" org_model "github.com/caos/zitadel/internal/org/model" "github.com/golang/protobuf/ptypes" ) @@ -35,3 +36,96 @@ func orgMemberFromModel(member *org_model.OrgMember) *OrgMember { Sequence: member.Sequence, } } + +func orgMemberSearchRequestToModel(request *OrgMemberSearchRequest) *org_model.OrgMemberSearchRequest { + return &org_model.OrgMemberSearchRequest{ + Limit: request.Limit, + Offset: request.Offset, + Queries: orgMemberSearchQueriesToModel(request.Queries), + } +} + +func orgMemberSearchQueriesToModel(queries []*OrgMemberSearchQuery) []*org_model.OrgMemberSearchQuery { + modelQueries := make([]*org_model.OrgMemberSearchQuery, len(queries)) + + for i, query := range queries { + modelQueries[i] = orgMemberSearchQueryToModel(query) + } + + return modelQueries +} + +func orgMemberSearchQueryToModel(query *OrgMemberSearchQuery) *org_model.OrgMemberSearchQuery { + return &org_model.OrgMemberSearchQuery{ + Key: orgMemberSearchKeyToModel(query.Key), + Method: orgMemberSearchMethodToModel(query.Method), + Value: query.Value, + } +} + +func orgMemberSearchKeyToModel(key OrgMemberSearchKey) org_model.OrgMemberSearchKey { + switch key { + case OrgMemberSearchKey_ORGMEMBERSEARCHKEY_EMAIL: + return org_model.ORGMEMBERSEARCHKEY_EMAIL + case OrgMemberSearchKey_ORGMEMBERSEARCHKEY_FIRST_NAME: + return org_model.ORGMEMBERSEARCHKEY_FIRST_NAME + case OrgMemberSearchKey_ORGMEMBERSEARCHKEY_LAST_NAME: + return org_model.ORGMEMBERSEARCHKEY_LAST_NAME + case OrgMemberSearchKey_ORGMEMBERSEARCHKEY_USER_ID: + return org_model.ORGMEMBERSEARCHKEY_USER_ID + default: + return org_model.ORGMEMBERSEARCHKEY_UNSPECIFIED + } +} + +func orgMemberSearchMethodToModel(key SearchMethod) model.SearchMethod { + switch key { + case SearchMethod_SEARCHMETHOD_CONTAINS: + return model.SEARCHMETHOD_CONTAINS + case SearchMethod_SEARCHMETHOD_CONTAINS_IGNORE_CASE: + return model.SEARCHMETHOD_CONTAINS_IGNORE_CASE + case SearchMethod_SEARCHMETHOD_EQUALS: + return model.SEARCHMETHOD_EQUALS + case SearchMethod_SEARCHMETHOD_EQUALS_IGNORE_CASE: + return model.SEARCHMETHOD_EQUALS_IGNORE_CASE + case SearchMethod_SEARCHMETHOD_STARTS_WITH: + return model.SEARCHMETHOD_STARTS_WITH + case SearchMethod_SEARCHMETHOD_STARTS_WITH_IGNORE_CASE: + return model.SEARCHMETHOD_STARTS_WITH_IGNORE_CASE + default: + return -1 + } +} + +func orgMemberSearchResponseFromModel(resp *org_model.OrgMemberSearchResponse) *OrgMemberSearchResponse { + return &OrgMemberSearchResponse{ + Limit: resp.Limit, + Offset: resp.Offset, + TotalResult: resp.TotalResult, + Result: orgMembersFromView(resp.Result), + } +} +func orgMembersFromView(viewMembers []*org_model.OrgMemberView) []*OrgMember { + members := make([]*OrgMember, len(viewMembers)) + + for i, member := range viewMembers { + members[i] = orgMemberFromView(member) + } + + return members +} + +func orgMemberFromView(member *org_model.OrgMemberView) *OrgMember { + changeDate, err := ptypes.TimestampProto(member.ChangeDate) + logging.Log("GRPC-S9LAZ").OnError(err).Debug("unable to parse changedate") + creationDate, err := ptypes.TimestampProto(member.CreationDate) + logging.Log("GRPC-oJN56").OnError(err).Debug("unable to parse creation date") + + return &OrgMember{ + ChangeDate: changeDate, + CreationDate: creationDate, + Roles: member.Roles, + Sequence: member.Sequence, + UserId: member.UserID, + } +} diff --git a/pkg/management/api/grpc/project_grant.go b/pkg/management/api/grpc/project_grant.go index ff880ca27a..1544bfc266 100644 --- a/pkg/management/api/grpc/project_grant.go +++ b/pkg/management/api/grpc/project_grant.go @@ -2,16 +2,12 @@ package grpc import ( "context" + "github.com/caos/zitadel/internal/api" grpc_util "github.com/caos/zitadel/internal/api/grpc" - "github.com/caos/zitadel/internal/errors" "github.com/golang/protobuf/ptypes/empty" ) -func (s *Server) GetProjectGrantMemberRoles(ctx context.Context, _ *empty.Empty) (*ProjectGrantMemberRoles, error) { - return nil, errors.ThrowUnimplemented(nil, "GRPC-mGo89", "Not implemented") -} - func (s *Server) SearchProjectGrants(ctx context.Context, in *ProjectGrantSearchRequest) (*ProjectGrantSearchResponse, error) { request := projectGrantSearchRequestsToModel(in) orgID := grpc_util.GetHeader(ctx, api.ZitadelOrgID) diff --git a/pkg/management/api/grpc/project_grant_member.go b/pkg/management/api/grpc/project_grant_member.go index b361557f30..1e3b4b949d 100644 --- a/pkg/management/api/grpc/project_grant_member.go +++ b/pkg/management/api/grpc/project_grant_member.go @@ -2,9 +2,14 @@ package grpc import ( "context" + "github.com/golang/protobuf/ptypes/empty" ) +func (s *Server) GetProjectGrantMemberRoles(ctx context.Context, _ *empty.Empty) (*ProjectGrantMemberRoles, error) { + return &ProjectGrantMemberRoles{Roles: s.project.GetProjectGrantMemberRoles()}, nil +} + func (s *Server) SearchProjectGrantMembers(ctx context.Context, in *ProjectGrantMemberSearchRequest) (*ProjectGrantMemberSearchResponse, error) { response, err := s.project.SearchProjectGrantMembers(ctx, projectGrantMemberSearchRequestsToModel(in)) if err != nil { diff --git a/pkg/management/api/grpc/project_member.go b/pkg/management/api/grpc/project_member.go index f8e3ed5437..9dd6b17247 100644 --- a/pkg/management/api/grpc/project_member.go +++ b/pkg/management/api/grpc/project_member.go @@ -2,12 +2,12 @@ package grpc import ( "context" - "github.com/caos/zitadel/internal/errors" + "github.com/golang/protobuf/ptypes/empty" ) func (s *Server) GetProjectMemberRoles(ctx context.Context, _ *empty.Empty) (*ProjectMemberRoles, error) { - return nil, errors.ThrowUnimplemented(nil, "GRPC-qw34d", "Not implemented") + return &ProjectMemberRoles{Roles: s.project.GetProjectMemberRoles()}, nil } func (s *Server) SearchProjectMembers(ctx context.Context, in *ProjectMemberSearchRequest) (*ProjectMemberSearchResponse, error) { diff --git a/pkg/management/api/grpc/server.go b/pkg/management/api/grpc/server.go index 7d0db9f5e8..f25c4309de 100644 --- a/pkg/management/api/grpc/server.go +++ b/pkg/management/api/grpc/server.go @@ -17,7 +17,6 @@ type Server struct { project repository.ProjectRepository policy repository.PolicyRepository org repository.OrgRepository - orgMember repository.OrgMemberRepository user repository.UserRepository usergrant repository.UserGrantRepository verifier *mgmt_auth.TokenVerifier @@ -30,7 +29,6 @@ func StartServer(conf grpc_util.ServerConfig, authZ auth.Config, repo repository project: repo, policy: repo, org: repo, - orgMember: repo, user: repo, usergrant: repo, authZ: authZ, diff --git a/pkg/management/management.go b/pkg/management/management.go index 491b26adcd..f1134ce22c 100644 --- a/pkg/management/management.go +++ b/pkg/management/management.go @@ -16,7 +16,11 @@ type Config struct { } func Start(ctx context.Context, config Config, authZ auth.Config, systemDefaults sd.SystemDefaults) { - repo, err := eventsourcing.Start(config.Repository, systemDefaults) + roles := make([]string, len(authZ.RolePermissionMappings)) + for i, role := range authZ.RolePermissionMappings { + roles[i] = role.Role + } + repo, err := eventsourcing.Start(config.Repository, systemDefaults, roles) logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app") api.Start(ctx, config.API, authZ, repo)