diff --git a/internal/api/authz/context.go b/internal/api/authz/context.go index c4b21dabaa..13eedcb081 100644 --- a/internal/api/authz/context.go +++ b/internal/api/authz/context.go @@ -2,6 +2,7 @@ package authz import ( "context" + "github.com/caos/zitadel/internal/errors" "github.com/caos/logging" ) @@ -33,6 +34,10 @@ type Grant struct { } func VerifyTokenAndWriteCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ context.Context, err error) { + err = t.ExistsOrg(ctx, orgID) + if err != nil { + return nil, errors.ThrowPermissionDenied(nil, "AUTH-Bs7Ds", "Organisation doesn't exist") + } userID, clientID, agentID, err := verifyAccessToken(ctx, token, t, method) if err != nil { return nil, err diff --git a/internal/api/authz/permissions_test.go b/internal/api/authz/permissions_test.go index 1109596f59..e2b9c3b984 100644 --- a/internal/api/authz/permissions_test.go +++ b/internal/api/authz/permissions_test.go @@ -27,6 +27,10 @@ func (v *testVerifier) ProjectIDByClientID(ctx context.Context, clientID string) return "", nil } +func (v *testVerifier) ExistsOrg(ctx context.Context, orgID string) error { + return nil +} + func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (string, error) { return "clientID", nil } diff --git a/internal/api/authz/token.go b/internal/api/authz/token.go index 280ab388db..4044216a79 100644 --- a/internal/api/authz/token.go +++ b/internal/api/authz/token.go @@ -23,6 +23,7 @@ type authZRepo interface { VerifierClientID(ctx context.Context, name string) (clientID string, err error) ResolveGrants(ctx context.Context) (grant *Grant, err error) ProjectIDByClientID(ctx context.Context, clientID string) (projectID string, err error) + ExistsOrg(ctx context.Context, orgID string) error } func Start(authZRepo authZRepo) (v *TokenVerifier) { @@ -91,6 +92,10 @@ func (v *TokenVerifier) GetProjectIDByClientID(ctx context.Context, clientID str return v.authZRepo.ProjectIDByClientID(ctx, clientID) } +func (v *TokenVerifier) ExistsOrg(ctx context.Context, orgID string) error { + return v.authZRepo.ExistsOrg(ctx, orgID) +} + func (v *TokenVerifier) CheckAuthMethod(method string) (Option, bool) { authOpt, ok := v.authMethods[method] return authOpt, ok diff --git a/internal/api/grpc/server/middleware/auth_interceptor_test.go b/internal/api/grpc/server/middleware/auth_interceptor_test.go index 8e2f753f6f..6cea063535 100644 --- a/internal/api/grpc/server/middleware/auth_interceptor_test.go +++ b/internal/api/grpc/server/middleware/auth_interceptor_test.go @@ -30,6 +30,9 @@ func (v *verifierMock) ResolveGrants(ctx context.Context) (*authz.Grant, error) func (v *verifierMock) ProjectIDByClientID(ctx context.Context, clientID string) (string, error) { return "", nil } +func (v *verifierMock) ExistsOrg(ctx context.Context, orgID string) error { + return nil +} func (v *verifierMock) VerifierClientID(ctx context.Context, appName string) (string, error) { return "", nil } diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index 5e5c7aff14..9c964cf069 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -48,6 +48,11 @@ func (repo *TokenVerifierRepo) ProjectIDByClientID(ctx context.Context, clientID return app.ProjectID, nil } +func (repo *TokenVerifierRepo) ExistsOrg(ctx context.Context, orgID string) error { + _, err := repo.View.OrgByID(orgID) + return err +} + func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName string) (string, error) { iam, err := repo.IamEvents.IamByID(ctx, repo.IamID) if err != nil { diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go index d5904f0435..d6feefef23 100644 --- a/internal/authz/repository/eventsourcing/handler/handler.go +++ b/internal/authz/repository/eventsourcing/handler/handler.go @@ -37,6 +37,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev iamEvents: repos.IamEvents, }, &Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}}, + &Org{handler: handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount}}, } } diff --git a/internal/authz/repository/eventsourcing/handler/org.go b/internal/authz/repository/eventsourcing/handler/org.go new file mode 100644 index 0000000000..10cdc18f1e --- /dev/null +++ b/internal/authz/repository/eventsourcing/handler/org.go @@ -0,0 +1,67 @@ +package handler + +import ( + "github.com/caos/logging" + es_models "github.com/caos/zitadel/internal/eventstore/models" + "github.com/caos/zitadel/internal/eventstore/spooler" + "github.com/caos/zitadel/internal/org/repository/eventsourcing" + "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" + org_model "github.com/caos/zitadel/internal/org/repository/view/model" + "time" +) + +type Org struct { + handler +} + +const ( + orgTable = "authz.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.CurrentSequence), nil +} + +func (o *Org) Reduce(event *es_models.Event) error { + org := new(org_model.OrgView) + + switch event.Type { + case model.OrgAdded: + err := org.AppendEvent(event) + if err != nil { + return err + } + case 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-8siWS", "id", event.AggregateID).WithError(spoolerErr).Warn("something went wrong in org handler") + return spooler.HandleError(event, spoolerErr, o.view.GetLatestOrgFailedEvent, o.view.ProcessedOrgFailedEvent, o.view.ProcessedOrgSequence, o.errorCountUntilSkip) +} diff --git a/internal/authz/repository/eventsourcing/view/org.go b/internal/authz/repository/eventsourcing/view/org.go new file mode 100644 index 0000000000..8b25c0dce3 --- /dev/null +++ b/internal/authz/repository/eventsourcing/view/org.go @@ -0,0 +1,44 @@ +package view + +import ( + "github.com/caos/zitadel/internal/org/model" + org_view "github.com/caos/zitadel/internal/org/repository/view" + org_model "github.com/caos/zitadel/internal/org/repository/view/model" + "github.com/caos/zitadel/internal/view/repository" +) + +const ( + orgTable = "authz.orgs" +) + +func (v *View) OrgByID(orgID string) (*org_model.OrgView, error) { + return org_view.OrgByID(v.Db, orgTable, orgID) +} + +func (v *View) SearchOrgs(req *model.OrgSearchRequest) ([]*org_model.OrgView, int, error) { + return org_view.SearchOrgs(v.Db, orgTable, req) +} + +func (v *View) PutOrg(org *org_model.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) (*repository.FailedEvent, error) { + return v.latestFailedEvent(orgTable, sequence) +} + +func (v *View) ProcessedOrgFailedEvent(failedEvent *repository.FailedEvent) error { + return v.saveFailedEvent(failedEvent) +} + +func (v *View) GetLatestOrgSequence() (*repository.CurrentSequence, error) { + return v.latestSequence(orgTable) +} + +func (v *View) ProcessedOrgSequence(eventSequence uint64) error { + return v.saveCurrentSequence(orgTable, eventSequence) +} diff --git a/internal/authz/repository/token_verifier.go b/internal/authz/repository/token_verifier.go index e856178233..137f7b3c1b 100644 --- a/internal/authz/repository/token_verifier.go +++ b/internal/authz/repository/token_verifier.go @@ -7,4 +7,5 @@ import ( type TokenVerifierRepository interface { VerifyAccessToken(ctx context.Context, appName string) (string, string, string, error) ProjectIDByClientID(ctx context.Context, clientID string) (string, error) + ExistsOrg(ctx context.Context, orgID string) error } diff --git a/migrations/cockroach/V1.6__authz_orgs.sql b/migrations/cockroach/V1.6__authz_orgs.sql new file mode 100644 index 0000000000..3ac9171c76 --- /dev/null +++ b/migrations/cockroach/V1.6__authz_orgs.sql @@ -0,0 +1,17 @@ +BEGIN; + +CREATE TABLE authz.orgs ( + id TEXT, + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + resource_owner TEXT, + org_state SMALLINT, + sequence BIGINT, + + domain TEXT, + name TEXT, + + PRIMARY KEY (id) +); + +COMMIT; \ No newline at end of file