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
This commit is contained in:
Silvan 2020-05-26 16:46:16 +02:00 committed by GitHub
parent a6aba86b54
commit 3025ac577b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1732 additions and 164 deletions

View File

@ -93,6 +93,7 @@ Admin:
CustomHeaders: CustomHeaders:
- x-zitadel- - x-zitadel-
Repository: Repository:
SearchLimit: 100
Eventstore: Eventstore:
ServiceName: 'Admin' ServiceName: 'Admin'
Repository: Repository:
@ -106,6 +107,16 @@ Admin:
Type: 'fastcache' Type: 'fastcache'
Config: Config:
MaxCacheSizeInByte: 10485760 #10mb 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: Console:
Port: 50050 Port: 50050

View File

@ -4,11 +4,12 @@ import (
"context" "context"
admin_model "github.com/caos/zitadel/internal/admin/model" 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"
"github.com/caos/zitadel/internal/eventstore/sdk" "github.com/caos/zitadel/internal/eventstore/sdk"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" 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" usr_es "github.com/caos/zitadel/internal/user/repository/eventsourcing"
) )
@ -16,6 +17,10 @@ type OrgRepo struct {
Eventstore eventstore.Eventstore Eventstore eventstore.Eventstore
OrgEventstore *org_es.OrgEventstore OrgEventstore *org_es.OrgEventstore
UserEventstore *usr_es.UserEventstore 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) { 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)) return repo.OrgEventstore.OrgByID(ctx, org_model.NewOrg(id))
} }
func (repo *OrgRepo) SearchOrgs(ctx context.Context) ([]*org_model.Org, error) { func (repo *OrgRepo) SearchOrgs(ctx context.Context, query *org_model.OrgSearchRequest) (*org_model.OrgSearchResult, error) {
return nil, errors.ThrowUnimplemented(nil, "EVENT-hFIHK", "search not implemented") 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) { func (repo *OrgRepo) IsOrgUnique(ctx context.Context, name, domain string) (isUnique bool, err error) {

View File

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

View File

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

View File

@ -6,8 +6,12 @@ import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/eventstore" "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/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" sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/config/types"
es_int "github.com/caos/zitadel/internal/eventstore" 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_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing" es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing"
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing" es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
@ -15,13 +19,14 @@ import (
) )
type Config struct { type Config struct {
Eventstore es_int.Config SearchLimit uint64
//View view.ViewConfig Eventstore es_int.Config
//Spooler spooler.SpoolerConfig View types.SQL
Spooler spooler.SpoolerConfig
} }
type EsRepository struct { type EsRepository struct {
//spooler *es_spooler.Spooler spooler *es_spol.Spooler
eventstore.OrgRepo eventstore.OrgRepo
} }
@ -31,15 +36,6 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
return nil, err 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{ iam, err := es_iam.StartIam(es_iam.IamConfig{
Eventstore: es, Eventstore: es,
Cache: conf.Eventstore.Cache, Cache: conf.Eventstore.Cache,
@ -66,15 +62,29 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults) (
return nil, err 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} eventstoreRepos := setup.EventstoreRepos{OrgEvents: org, UserEvents: user, ProjectEvents: project, IamEvents: iam}
err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx) err = setup.StartSetup(systemDefaults, eventstoreRepos).Execute(ctx)
logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup") logging.Log("SERVE-k280HZ").OnError(err).Panic("failed to execute setup")
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient)
return &EsRepository{ return &EsRepository{
spooler: spool,
OrgRepo: eventstore.OrgRepo{ OrgRepo: eventstore.OrgRepo{
Eventstore: es, Eventstore: es,
OrgEventstore: org, OrgEventstore: org,
UserEventstore: user, UserEventstore: user,
View: view,
SearchLimit: conf.SearchLimit,
}, },
}, nil }, nil
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,5 +11,5 @@ type OrgRepository interface {
SetUpOrg(context.Context, *admin_model.SetupOrg) (*admin_model.SetupOrg, error) SetUpOrg(context.Context, *admin_model.SetupOrg) (*admin_model.SetupOrg, error)
IsOrgUnique(ctx context.Context, name, domain string) (bool, error) IsOrgUnique(ctx context.Context, name, domain string) (bool, error)
OrgByID(ctx context.Context, id string) (*org_model.Org, 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)
} }

View File

@ -2,14 +2,20 @@ package eventstore
import ( import (
"context" "context"
"strings"
"github.com/caos/zitadel/internal/errors" "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_model "github.com/caos/zitadel/internal/org/model"
org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing" org_es "github.com/caos/zitadel/internal/org/repository/eventsourcing"
"github.com/caos/zitadel/internal/org/repository/view"
) )
type OrgRepository struct { type OrgRepository struct {
SearchLimit uint64
*org_es.OrgEventstore *org_es.OrgEventstore
View *mgmt_view.View
Roles []string
} }
func (repo *OrgRepository) OrgByID(ctx context.Context, id string) (*org_model.Org, error) { 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) return repo.OrgEventstore.OrgByID(ctx, org)
} }
func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.Org, error) { func (repo *OrgRepository) OrgByDomainGlobal(ctx context.Context, domain string) (*org_model.OrgView, error) {
return nil, errors.ThrowUnimplemented(nil, "EVENT-GQoS8", "not implemented") 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) { 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) member := org_model.NewOrgMember(orgID, userID)
return repo.OrgEventstore.RemoveOrgMember(ctx, member) 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
}

View File

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

View File

@ -2,6 +2,8 @@ package eventstore
import ( import (
"context" "context"
"strings"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/project/repository/view/model" "github.com/caos/zitadel/internal/project/repository/view/model"
@ -13,6 +15,7 @@ type ProjectRepo struct {
SearchLimit uint64 SearchLimit uint64
ProjectEvents *proj_event.ProjectEventstore ProjectEvents *proj_event.ProjectEventstore
View *view.View View *view.View
Roles []string
} }
func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *proj_model.Project, err error) { 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), Result: model.ProjectGrantMembersToModel(members),
}, nil }, 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
}

View File

@ -9,8 +9,9 @@ import (
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/project/model" org_model "github.com/caos/zitadel/internal/org/model"
"github.com/caos/zitadel/internal/project/repository/eventsourcing" 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" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/project/repository/view/model" view_model "github.com/caos/zitadel/internal/project/repository/view/model"
@ -20,6 +21,7 @@ type GrantedProject struct {
handler handler
eventstore eventstore.Eventstore eventstore eventstore.Eventstore
projectEvents *proj_event.ProjectEventstore projectEvents *proj_event.ProjectEventstore
orgEvents *org_event.OrgEventstore
} }
const ( const (
@ -37,7 +39,7 @@ func (p *GrantedProject) EventQuery() (*models.SearchQuery, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return eventsourcing.ProjectQuery(sequence), nil return proj_event.ProjectQuery(sequence), nil
} }
func (p *GrantedProject) Process(event *models.Event) (err error) { func (p *GrantedProject) Process(event *models.Event) (err error) {
@ -71,7 +73,12 @@ func (p *GrantedProject) Process(event *models.Event) (err error) {
return err return err
} }
grantedProject.Name = project.Name 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: case es_model.ProjectGrantChanged:
grant := new(view_model.ProjectGrant) grant := new(view_model.ProjectGrant)
err := grant.SetData(event) err := grant.SetData(event)
@ -99,11 +106,12 @@ func (p *GrantedProject) Process(event *models.Event) (err error) {
return p.view.PutGrantedProject(grantedProject) return p.view.PutGrantedProject(grantedProject)
} }
func (p *GrantedProject) getOrg(orgID string) { func (p *GrantedProject) fillOrgData(grantedProject *view_model.GrantedProjectView, org *org_model.Org) {
//TODO: Get 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) return p.projectEvents.ProjectByID(context.Background(), projectID)
} }

View File

@ -1,13 +1,15 @@
package handler package handler
import ( import (
"time"
"github.com/caos/zitadel/internal/config/types" "github.com/caos/zitadel/internal/config/types"
"github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view" "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" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
"time"
) )
type Configs map[string]*Config type Configs map[string]*Config
@ -26,17 +28,20 @@ type handler struct {
type EventstoreRepos struct { type EventstoreRepos struct {
ProjectEvents *proj_event.ProjectEventstore ProjectEvents *proj_event.ProjectEventstore
UserEvents *usr_event.UserEventstore 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 { func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos) []spooler.Handler {
return []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}, &ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, projectEvents: repos.ProjectEvents},
&ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, userEvents: repos.UserEvents}, &ProjectMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount}, userEvents: repos.UserEvents},
&ProjectGrantMember{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), 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}}, &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}, projectEvents: repos.ProjectEvents},
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}}, &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}, &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},
} }
} }

View File

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

View File

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

View File

@ -2,7 +2,11 @@ package handler
import ( import (
"context" "context"
"time"
es_models "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"
org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing"
proj_model "github.com/caos/zitadel/internal/project/model" proj_model "github.com/caos/zitadel/internal/project/model"
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing" proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model" 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_events "github.com/caos/zitadel/internal/user/repository/eventsourcing"
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model"
"time"
"github.com/caos/logging" "github.com/caos/logging"
@ -25,6 +28,7 @@ type UserGrant struct {
eventstore eventstore.Eventstore eventstore eventstore.Eventstore
projectEvents *proj_event.ProjectEventstore projectEvents *proj_event.ProjectEventstore
userEvents *usr_events.UserEventstore userEvents *usr_events.UserEventstore
orgEvents *org_events.OrgEventstore
} }
const ( const (
@ -67,7 +71,7 @@ func (u *UserGrant) processUserGrant(event *models.Event) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = u.fillData(grant) err = u.fillData(grant, event.ResourceOwner)
case grant_es_model.UserGrantChanged, case grant_es_model.UserGrantChanged,
grant_es_model.UserGrantDeactivated, grant_es_model.UserGrantDeactivated,
grant_es_model.UserGrantReactivated: grant_es_model.UserGrantReactivated:
@ -133,7 +137,7 @@ func (u *UserGrant) processProject(event *models.Event) (err error) {
return nil 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) user, err := u.userEvents.UserByID(context.Background(), grant.UserID)
if err != nil { if err != nil {
return err return err
@ -144,7 +148,12 @@ func (u *UserGrant) fillData(grant *view_model.UserGrantView) (err error) {
return err return err
} }
u.fillProjectData(grant, project) 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 return nil
} }
@ -159,8 +168,9 @@ func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *pr
grant.ProjectName = project.Name grant.ProjectName = project.Name
} }
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView) { func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
//TODO: get ORG grant.OrgDomain = org.Domain
grant.OrgName = org.Name
} }
func (u *UserGrant) OnError(event *models.Event, err error) error { func (u *UserGrant) OnError(event *models.Event, err error) error {

View File

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

View File

@ -34,7 +34,7 @@ type EsRepository struct {
eventstore.PolicyRepo 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) es, err := es_int.Start(conf.Eventstore)
if err != nil { if err != nil {
return nil, err 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}) 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) spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos)
return &EsRepository{ return &EsRepository{
spooler: spool, spooler: spool,
OrgRepository: eventstore.OrgRepository{org}, OrgRepository: eventstore.OrgRepository{conf.SearchLimit, org, view, roles},
ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view}, ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view, roles},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, view}, UserRepo: eventstore.UserRepo{conf.SearchLimit, user, view},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view}, UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view},
PolicyRepo: eventstore.PolicyRepo{policy}, PolicyRepo: eventstore.PolicyRepo{policy},

View File

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

View File

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

View File

@ -8,14 +8,15 @@ import (
type OrgRepository interface { type OrgRepository interface {
OrgByID(ctx context.Context, id string) (*org_model.Org, error) 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) UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error)
DeactivateOrg(ctx context.Context, id string) (*org_model.Org, error) DeactivateOrg(ctx context.Context, id string) (*org_model.Org, error)
ReactivateOrg(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) AddOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error)
ChangeOrgMember(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 RemoveOrgMember(ctx context.Context, orgID, userID string) error
GetOrgMemberRoles() []string
} }

View File

@ -2,6 +2,7 @@ package repository
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/project/model" "github.com/caos/zitadel/internal/project/model"
) )
@ -18,6 +19,7 @@ type ProjectRepository interface {
ChangeProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error) ChangeProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error)
RemoveProjectMember(ctx context.Context, projectID, userID string) error RemoveProjectMember(ctx context.Context, projectID, userID string) error
SearchProjectMembers(ctx context.Context, request *model.ProjectMemberSearchRequest) (*model.ProjectMemberSearchResponse, error) SearchProjectMembers(ctx context.Context, request *model.ProjectMemberSearchRequest) (*model.ProjectMemberSearchResponse, error)
GetProjectMemberRoles() []string
AddProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error) AddProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error)
ChangeProjectRole(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) AddProjectGrantMember(ctx context.Context, member *model.ProjectGrantMember) (*model.ProjectGrantMember, error)
ChangeProjectGrantMember(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 RemoveProjectGrantMember(ctx context.Context, projectID, grantID, userID string) error
GetProjectGrantMemberRoles() []string
} }

View File

@ -5,7 +5,6 @@ type Repository interface {
ProjectRepository ProjectRepository
PolicyRepository PolicyRepository
OrgRepository OrgRepository
OrgMemberRepository
UserRepository UserRepository
UserGrantRepository UserGrantRepository
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,15 +55,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto) passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto)
aesOtpCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey) aesOtpCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey)
passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost) 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{ return &UserEventstore{
Eventstore: conf.Eventstore, Eventstore: conf.Eventstore,
userCache: userCache, userCache: userCache,
@ -72,9 +64,14 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
EmailVerificationCode: emailVerificationCode, EmailVerificationCode: emailVerificationCode,
PhoneVerificationCode: phoneVerificationCode, PhoneVerificationCode: phoneVerificationCode,
PasswordVerificationCode: passwordVerificationCode, PasswordVerificationCode: passwordVerificationCode,
Multifactors: mfa, Multifactors: global_model.Multifactors{
PasswordAlg: passwordAlg, OTP: global_model.OTP{
validateTOTP: totp.Validate, CryptoMFA: aesOtpCrypto,
Issuer: systemDefaults.Multifactors.OTP.Issuer,
},
},
PasswordAlg: passwordAlg,
validateTOTP: totp.Validate,
}, nil }, nil
} }

View File

@ -2,13 +2,14 @@ package model
import ( import (
"encoding/json" "encoding/json"
"time"
"github.com/caos/logging" "github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors" caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/usergrant/model" "github.com/caos/zitadel/internal/usergrant/model"
es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model"
"github.com/lib/pq" "github.com/lib/pq"
"time"
) )
const ( const (

View File

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

View File

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

View File

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

View File

@ -2,6 +2,10 @@ package grpc
import ( import (
"context" "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) { 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) { 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 { if err != nil {
return nil, err return nil, err
} }
return &OrgSearchResponse{Result: orgsFromModel(orgs), return &OrgSearchResponse{
Limit: request.Limit, Result: orgViewsFromModel(result.Result),
Offset: request.Offset, Limit: request.Limit,
// TotalResult: , TODO: total result from search Offset: request.Offset,
TotalResult: result.TotalResult,
}, nil }, nil
} }
@ -37,3 +42,57 @@ func (s *Server) SetUpOrg(ctx context.Context, orgSetUp *OrgSetUpRequest) (_ *Or
} }
return setUpOrgResponseFromModel(setUp), err 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
}
}

View File

@ -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)) result := make([]*Org, len(orgs))
for i, org := range orgs { for i, org := range orgs {
result[i] = orgFromModel(org) result[i] = orgViewFromModel(org)
} }
return result 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 { func userFromModel(user *usr_model.User) *User {
creationDate, err := ptypes.TimestampProto(user.CreationDate) creationDate, err := ptypes.TimestampProto(user.CreationDate)
logging.Log("GRPC-8duwe").OnError(err).Debug("unable to parse timestamp") logging.Log("GRPC-8duwe").OnError(err).Debug("unable to parse timestamp")

View File

@ -19,7 +19,7 @@ func (s *Server) GetOrgByDomainGlobal(ctx context.Context, in *OrgDomain) (*Org,
if err != nil { if err != nil {
return nil, err return nil, err
} }
return orgFromModel(org), nil return orgFromView(org), nil
} }
func (s *Server) DeactivateOrg(ctx context.Context, in *OrgID) (*Org, error) { func (s *Server) DeactivateOrg(ctx context.Context, in *OrgID) (*Org, error) {

View File

@ -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 { func orgStateFromModel(state org_model.OrgState) OrgState {
switch state { switch state {
case org_model.ORGSTATE_ACTIVE: case org_model.ORGSTATE_ACTIVE:

View File

@ -3,22 +3,25 @@ package grpc
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
func (s *Server) GetOrgMemberRoles(ctx context.Context, _ *empty.Empty) (*OrgMemberRoles, error) { 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) { 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) { func (s *Server) AddOrgMember(ctx context.Context, member *AddOrgMemberRequest) (*OrgMember, error) {
repositoryMember := addOrgMemberToModel(member) repositoryMember := addOrgMemberToModel(member)
addedMember, err := s.orgMember.AddOrgMember(ctx, repositoryMember) addedMember, err := s.org.AddOrgMember(ctx, repositoryMember)
if err != nil { if err != nil {
return nil, err 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) { func (s *Server) ChangeOrgMember(ctx context.Context, member *ChangeOrgMemberRequest) (*OrgMember, error) {
repositoryMember := changeOrgMemberToModel(member) repositoryMember := changeOrgMemberToModel(member)
changedMember, err := s.orgMember.ChangeOrgMember(ctx, repositoryMember) changedMember, err := s.org.ChangeOrgMember(ctx, repositoryMember)
if err != nil { if err != nil {
return nil, err 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) { 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 return &empty.Empty{}, err
} }

View File

@ -2,6 +2,7 @@ package grpc
import ( import (
"github.com/caos/logging" "github.com/caos/logging"
"github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model" org_model "github.com/caos/zitadel/internal/org/model"
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes"
) )
@ -35,3 +36,96 @@ func orgMemberFromModel(member *org_model.OrgMember) *OrgMember {
Sequence: member.Sequence, 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,
}
}

View File

@ -2,16 +2,12 @@ package grpc
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/api" "github.com/caos/zitadel/internal/api"
grpc_util "github.com/caos/zitadel/internal/api/grpc" grpc_util "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "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) { func (s *Server) SearchProjectGrants(ctx context.Context, in *ProjectGrantSearchRequest) (*ProjectGrantSearchResponse, error) {
request := projectGrantSearchRequestsToModel(in) request := projectGrantSearchRequestsToModel(in)
orgID := grpc_util.GetHeader(ctx, api.ZitadelOrgID) orgID := grpc_util.GetHeader(ctx, api.ZitadelOrgID)

View File

@ -2,9 +2,14 @@ package grpc
import ( import (
"context" "context"
"github.com/golang/protobuf/ptypes/empty" "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) { func (s *Server) SearchProjectGrantMembers(ctx context.Context, in *ProjectGrantMemberSearchRequest) (*ProjectGrantMemberSearchResponse, error) {
response, err := s.project.SearchProjectGrantMembers(ctx, projectGrantMemberSearchRequestsToModel(in)) response, err := s.project.SearchProjectGrantMembers(ctx, projectGrantMemberSearchRequestsToModel(in))
if err != nil { if err != nil {

View File

@ -2,12 +2,12 @@ package grpc
import ( import (
"context" "context"
"github.com/caos/zitadel/internal/errors"
"github.com/golang/protobuf/ptypes/empty" "github.com/golang/protobuf/ptypes/empty"
) )
func (s *Server) GetProjectMemberRoles(ctx context.Context, _ *empty.Empty) (*ProjectMemberRoles, error) { 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) { func (s *Server) SearchProjectMembers(ctx context.Context, in *ProjectMemberSearchRequest) (*ProjectMemberSearchResponse, error) {

View File

@ -17,7 +17,6 @@ type Server struct {
project repository.ProjectRepository project repository.ProjectRepository
policy repository.PolicyRepository policy repository.PolicyRepository
org repository.OrgRepository org repository.OrgRepository
orgMember repository.OrgMemberRepository
user repository.UserRepository user repository.UserRepository
usergrant repository.UserGrantRepository usergrant repository.UserGrantRepository
verifier *mgmt_auth.TokenVerifier verifier *mgmt_auth.TokenVerifier
@ -30,7 +29,6 @@ func StartServer(conf grpc_util.ServerConfig, authZ auth.Config, repo repository
project: repo, project: repo,
policy: repo, policy: repo,
org: repo, org: repo,
orgMember: repo,
user: repo, user: repo,
usergrant: repo, usergrant: repo,
authZ: authZ, authZ: authZ,

View File

@ -16,7 +16,11 @@ type Config struct {
} }
func Start(ctx context.Context, config Config, authZ auth.Config, systemDefaults sd.SystemDefaults) { 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") logging.Log("MAIN-9uBxp").OnError(err).Panic("unable to start app")
api.Start(ctx, config.API, authZ, repo) api.Start(ctx, config.API, authZ, repo)