diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html
index c4436c0f6d..2a3e7f4117 100644
--- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html
+++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html
@@ -89,6 +89,9 @@
{{'PROJECT.ROLE.CHECK' | translate}}
{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}
+
+ {{'PROJECT.HAS_PROJECT' | translate}}
+ {{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}
-
\ No newline at end of file
+
diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts
index e763beea8b..e2bf69d7de 100644
--- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts
+++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.ts
@@ -185,6 +185,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
req.setName(this.project.name);
req.setProjectRoleAssertion(this.project.projectRoleAssertion);
req.setProjectRoleCheck(this.project.projectRoleCheck);
+ req.setHasProjectCheck(this.project.hasProjectCheck);
this.mgmtService.updateProject(req).then(() => {
this.toast.showInfo('PROJECT.TOAST.UPDATED', true);
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json
index ebf9dbcb2c..ed2a6f5015 100644
--- a/console/src/assets/i18n/de.json
+++ b/console/src/assets/i18n/de.json
@@ -1034,6 +1034,8 @@
"CHECK": "Rollen bei Authentisierung prüfen",
"CHECK_DESCRIPTION": "Ist das Attribut gesetzt, kann ein Benutzer nur mit einem entsprechenden Rolle authentifiziert werden."
},
+ "HAS_PROJECT": "Projektbesitz bei Authentisierung prüfen",
+ "HAS_PROJECT_DESCRIPTION": "Es wird überprüft, ob die Organisation des Benutzers über dieses Projekt verfügt. Wenn nicht, kann der Benutzer nicht authentifiziert werden.",
"TABLE": {
"TOTAL": "Einträge gesamt:",
"SELECTION": "ausgewählt",
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json
index 96c6e89283..28056c0871 100644
--- a/console/src/assets/i18n/en.json
+++ b/console/src/assets/i18n/en.json
@@ -1036,6 +1036,8 @@
"CHECK": "Check roles on Authentication",
"CHECK_DESCRIPTION": "If set, users are only allowed to authenticate if any role is assigned to their account."
},
+ "HAS_PROJECT": "Check for Project on Authentication",
+ "HAS_PROJECT_DESCRIPTION": "It is checked whether the user's organisation has this project. If not, the user cannot be authenticated.",
"TABLE": {
"TOTAL": "Entries total:",
"SELECTION": "Selected Elements",
diff --git a/docs/docs/apis/proto/management.md b/docs/docs/apis/proto/management.md
index ae1260fa8c..2d8ffd2633 100644
--- a/docs/docs/apis/proto/management.md
+++ b/docs/docs/apis/proto/management.md
@@ -3276,6 +3276,7 @@ This is an empty request
| name | string | - | string.min_len: 1
string.max_len: 200
|
| project_role_assertion | bool | - | |
| project_role_check | bool | - | |
+| has_project_check | bool | - | |
@@ -7520,6 +7521,7 @@ This is an empty request
| name | string | - | string.min_len: 1
string.max_len: 200
|
| project_role_assertion | bool | - | |
| project_role_check | bool | - | |
+| has_project_check | bool | - | |
diff --git a/docs/docs/apis/proto/project.md b/docs/docs/apis/proto/project.md
index ce7ed9c2c8..d925165017 100644
--- a/docs/docs/apis/proto/project.md
+++ b/docs/docs/apis/proto/project.md
@@ -65,6 +65,7 @@ title: zitadel/project.proto
| state | ProjectState | - | |
| project_role_assertion | bool | describes if roles of user should be added in token | |
| project_role_check | bool | ZITADEL checks if the user has at least one on this project | |
+| has_project_check | bool | ZITADEL checks if the org of the user has permission to this project | |
diff --git a/docs/docs/apis/proto/text.md b/docs/docs/apis/proto/text.md
index a2a0777961..9361d48ee3 100644
--- a/docs/docs/apis/proto/text.md
+++ b/docs/docs/apis/proto/text.md
@@ -39,6 +39,32 @@ title: zitadel/text.proto
+### ExternalRegistrationUserOverviewScreenText
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| title | string | - | string.max_len: 200
|
+| description | string | - | string.max_len: 500
|
+| email_label | string | - | string.max_len: 200
|
+| username_label | string | - | string.max_len: 200
|
+| firstname_label | string | - | string.max_len: 200
|
+| lastname_label | string | - | string.max_len: 200
|
+| nickname_label | string | - | string.max_len: 200
|
+| language_label | string | - | string.max_len: 200
|
+| phone_label | string | - | string.max_len: 200
|
+| tos_and_privacy_label | string | - | string.max_len: 200
|
+| tos_confirm | string | - | string.max_len: 200
|
+| tos_link_text | string | - | string.max_len: 200
|
+| tos_confirm_and | string | - | string.max_len: 200
|
+| privacy_link_text | string | - | string.max_len: 200
|
+| back_button_text | string | - | string.max_len: 200
|
+| next_button_text | string | - | string.max_len: 200
|
+
+
+
+
### ExternalUserNotFoundScreenText
@@ -49,6 +75,11 @@ title: zitadel/text.proto
| description | string | - | string.max_len: 500
|
| link_button_text | string | - | string.max_len: 100
|
| auto_register_button_text | string | - | string.max_len: 100
|
+| tos_and_privacy_label | string | - | string.max_len: 200
|
+| tos_confirm | string | - | string.max_len: 200
|
+| tos_link_text | string | - | string.max_len: 200
|
+| privacy_link_text | string | - | string.max_len: 200
|
+| tos_confirm_and | string | - | string.max_len: 200
|
@@ -246,6 +277,7 @@ title: zitadel/text.proto
| passwordless_prompt_text | PasswordlessPromptScreenText | - | |
| passwordless_registration_text | PasswordlessRegistrationScreenText | - | |
| passwordless_registration_done_text | PasswordlessRegistrationDoneScreenText | - | |
+| external_registration_user_overview_text | ExternalRegistrationUserOverviewScreenText | - | |
@@ -405,6 +437,7 @@ title: zitadel/text.proto
| title | string | - | string.max_len: 200
|
| description | string | - | string.max_len: 500
|
| next_button_text | string | - | string.max_len: 100
|
+| cancel_button_text | string | - | string.max_len: 100
|
diff --git a/internal/api/grpc/management/project_converter.go b/internal/api/grpc/management/project_converter.go
index 703d151a15..ba18827009 100644
--- a/internal/api/grpc/management/project_converter.go
+++ b/internal/api/grpc/management/project_converter.go
@@ -15,6 +15,7 @@ func ProjectCreateToDomain(req *mgmt_pb.AddProjectRequest) *domain.Project {
Name: req.Name,
ProjectRoleAssertion: req.ProjectRoleAssertion,
ProjectRoleCheck: req.ProjectRoleCheck,
+ HasProjectCheck: req.HasProjectCheck,
}
}
@@ -26,6 +27,7 @@ func ProjectUpdateToDomain(req *mgmt_pb.UpdateProjectRequest) *domain.Project {
Name: req.Name,
ProjectRoleAssertion: req.ProjectRoleAssertion,
ProjectRoleCheck: req.ProjectRoleCheck,
+ HasProjectCheck: req.HasProjectCheck,
}
}
diff --git a/internal/api/grpc/project/converter.go b/internal/api/grpc/project/converter.go
index 1aa3b56845..28420e707b 100644
--- a/internal/api/grpc/project/converter.go
+++ b/internal/api/grpc/project/converter.go
@@ -15,6 +15,7 @@ func ProjectToPb(project *proj_model.ProjectView) *proj_pb.Project {
State: projectStateToPb(project.State),
ProjectRoleAssertion: project.ProjectRoleAssertion,
ProjectRoleCheck: project.ProjectRoleCheck,
+ HasProjectCheck: project.HasProjectCheck,
}
}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index 2148a21c19..f5fa340ace 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -44,6 +44,7 @@ type AuthRequestRepo struct {
LockoutPolicyViewProvider lockoutPolicyViewProvider
IDPProviderViewProvider idpProviderViewProvider
UserGrantProvider userGrantProvider
+ ProjectProvider projectProvider
IdGenerator id.Generator
@@ -96,6 +97,11 @@ type userGrantProvider interface {
UserGrantsByProjectAndUserID(string, string) ([]*grant_view_model.UserGrantView, error)
}
+type projectProvider interface {
+ ApplicationByClientID(context.Context, string) (*project_view_model.ApplicationView, error)
+ OrgProjectMappingByIDs(orgID, projectID string) (*project_view_model.OrgProjectMapping, error)
+}
+
func (repo *AuthRequestRepo) Health(ctx context.Context) error {
return repo.AuthRequests.Health(ctx)
}
@@ -680,7 +686,15 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
}
//PLANNED: consent step
- missing, err := userGrantRequired(ctx, request, user, repo.UserGrantProvider)
+ missing, err := projectRequired(ctx, request, repo.ProjectProvider)
+ if err != nil {
+ return nil, err
+ }
+ if missing {
+ return append(steps, &domain.ProjectRequiredStep{}), nil
+ }
+
+ missing, err = userGrantRequired(ctx, request, user, repo.UserGrantProvider)
if err != nil {
return nil, err
}
@@ -1081,6 +1095,7 @@ func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*domain.ExternalUser,
}
return true
}
+
func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
var app *project_view_model.ApplicationView
switch request.Request.Type() {
@@ -1101,3 +1116,27 @@ func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *u
}
return len(grants) == 0, nil
}
+
+func projectRequired(ctx context.Context, request *domain.AuthRequest, projectProvider projectProvider) (_ bool, err error) {
+ var app *project_view_model.ApplicationView
+ switch request.Request.Type() {
+ case domain.AuthRequestTypeOIDC:
+ app, err = projectProvider.ApplicationByClientID(ctx, request.ApplicationID)
+ if err != nil {
+ return false, err
+ }
+ default:
+ return false, errors.ThrowPreconditionFailed(nil, "EVENT-dfrw2", "Errors.AuthRequest.RequestTypeNotSupported")
+ }
+ if !app.HasProjectCheck {
+ return false, nil
+ }
+ _, err = projectProvider.OrgProjectMappingByIDs(request.UserOrgID, app.ProjectID)
+ if errors.IsNotFound(err) {
+ return true, nil
+ }
+ if err != nil {
+ return false, err
+ }
+ return false, nil
+}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
index 283502264c..8f4ee22a29 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
@@ -6,9 +6,10 @@ import (
"testing"
"time"
- "github.com/caos/zitadel/internal/crypto"
"github.com/stretchr/testify/assert"
+ "github.com/caos/zitadel/internal/crypto"
+
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/auth_request/repository/cache"
@@ -227,6 +228,22 @@ func (m *mockUserGrants) UserGrantsByProjectAndUserID(s string, s2 string) ([]*g
return grants, nil
}
+type mockProject struct {
+ hasProject bool
+ projectCheck bool
+}
+
+func (m *mockProject) ApplicationByClientID(ctx context.Context, s string) (*proj_view_model.ApplicationView, error) {
+ return &proj_view_model.ApplicationView{HasProjectCheck: m.projectCheck}, nil
+}
+
+func (m *mockProject) OrgProjectMappingByIDs(orgID, projectID string) (*proj_view_model.OrgProjectMapping, error) {
+ if m.hasProject {
+ return &proj_view_model.OrgProjectMapping{OrgID: orgID, ProjectID: projectID}, nil
+ }
+ return nil, errors.ThrowNotFound(nil, "ERROR", "error")
+}
+
func TestAuthRequestRepo_nextSteps(t *testing.T) {
type fields struct {
AuthRequests *cache.AuthRequestCache
@@ -236,6 +253,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider userEventProvider
orgViewProvider orgViewProvider
userGrantProvider userGrantProvider
+ projectProvider projectProvider
loginPolicyProvider loginPolicyViewProvider
lockoutPolicyProvider lockoutPolicyViewProvider
PasswordCheckLifeTime time.Duration
@@ -684,6 +702,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{},
loginPolicyProvider: &mockLoginPolicy{
policy: &iam_view_model.LoginPolicyView{},
},
@@ -741,6 +760,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
@@ -971,6 +991,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
@@ -1004,6 +1025,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
@@ -1041,6 +1063,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true,
userGrants: 0,
},
+ projectProvider: &mockProject{},
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
@@ -1078,6 +1101,83 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
roleCheck: true,
userGrants: 2,
},
+ projectProvider: &mockProject{},
+ lockoutPolicyProvider: &mockLockoutPolicy{
+ policy: &iam_view_model.LockoutPolicyView{
+ ShowLockOutFailures: true,
+ },
+ },
+ PasswordCheckLifeTime: 10 * 24 * time.Hour,
+ SecondFactorCheckLifeTime: 18 * time.Hour,
+ },
+ args{&domain.AuthRequest{
+ UserID: "UserID",
+ Prompt: []domain.Prompt{domain.PromptNone},
+ Request: &domain.AuthRequestOIDC{},
+ LoginPolicy: &domain.LoginPolicy{
+ SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
+ },
+ }, true},
+ []domain.NextStep{&domain.RedirectToCallbackStep{}},
+ nil,
+ },
+ {
+ "prompt none, checkLoggedIn true, authenticated and required project missing, project required step",
+ fields{
+ userSessionViewProvider: &mockViewUserSession{
+ PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
+ SecondFactorVerification: time.Now().UTC().Add(-5 * time.Minute),
+ },
+ userViewProvider: &mockViewUser{
+ PasswordSet: true,
+ IsEmailVerified: true,
+ MFAMaxSetUp: int32(model.MFALevelSecondFactor),
+ },
+ userEventProvider: &mockEventUser{},
+ orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
+ userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{
+ projectCheck: true,
+ hasProject: false,
+ },
+ lockoutPolicyProvider: &mockLockoutPolicy{
+ policy: &iam_view_model.LockoutPolicyView{
+ ShowLockOutFailures: true,
+ },
+ },
+ PasswordCheckLifeTime: 10 * 24 * time.Hour,
+ SecondFactorCheckLifeTime: 18 * time.Hour,
+ },
+ args{&domain.AuthRequest{
+ UserID: "UserID",
+ Prompt: []domain.Prompt{domain.PromptNone},
+ Request: &domain.AuthRequestOIDC{},
+ LoginPolicy: &domain.LoginPolicy{
+ SecondFactors: []domain.SecondFactorType{domain.SecondFactorTypeOTP},
+ },
+ }, true},
+ []domain.NextStep{&domain.ProjectRequiredStep{}},
+ nil,
+ },
+ {
+ "prompt none, checkLoggedIn true, authenticated and required project exist, redirect to callback step",
+ fields{
+ userSessionViewProvider: &mockViewUserSession{
+ PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
+ SecondFactorVerification: time.Now().UTC().Add(-5 * time.Minute),
+ },
+ userViewProvider: &mockViewUser{
+ PasswordSet: true,
+ IsEmailVerified: true,
+ MFAMaxSetUp: int32(model.MFALevelSecondFactor),
+ },
+ userEventProvider: &mockEventUser{},
+ orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
+ userGrantProvider: &mockUserGrants{},
+ projectProvider: &mockProject{
+ projectCheck: true,
+ hasProject: true,
+ },
lockoutPolicyProvider: &mockLockoutPolicy{
policy: &iam_view_model.LockoutPolicyView{
ShowLockOutFailures: true,
@@ -1172,6 +1272,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
UserEventProvider: tt.fields.userEventProvider,
OrgViewProvider: tt.fields.orgViewProvider,
UserGrantProvider: tt.fields.userGrantProvider,
+ ProjectProvider: tt.fields.projectProvider,
LoginPolicyViewProvider: tt.fields.loginPolicyProvider,
LockoutPolicyViewProvider: tt.fields.lockoutPolicyProvider,
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
diff --git a/internal/auth/repository/eventsourcing/handler/application.go b/internal/auth/repository/eventsourcing/handler/application.go
index 7ef1d6fbba..65e75bdf71 100644
--- a/internal/auth/repository/eventsourcing/handler/application.go
+++ b/internal/auth/repository/eventsourcing/handler/application.go
@@ -82,6 +82,7 @@ func (a *Application) Reduce(event *models.Event) (err error) {
return err
}
app.ProjectRoleCheck = project.ProjectRoleCheck
+ app.HasProjectCheck = project.HasProjectCheck
app.ProjectRoleAssertion = project.ProjectRoleAssertion
err = app.AppendEvent(event)
diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go
index 67a2beae73..5c3daac5d8 100644
--- a/internal/auth/repository/eventsourcing/handler/handler.go
+++ b/internal/auth/repository/eventsourcing/handler/handler.go
@@ -74,6 +74,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newCustomText(handler{view, bulkLimit, configs.cycleDuration("CustomTexts"), errorCount, es}),
newMetadata(handler{view, bulkLimit, configs.cycleDuration("Metadata"), errorCount, es}),
newLockoutPolicy(handler{view, bulkLimit, configs.cycleDuration("LockoutPolicy"), errorCount, es}),
+ newOrgProjectMapping(handler{view, bulkLimit, configs.cycleDuration("OrgProjectMapping"), errorCount, es}),
}
}
diff --git a/internal/auth/repository/eventsourcing/handler/org_project_mapping.go b/internal/auth/repository/eventsourcing/handler/org_project_mapping.go
new file mode 100644
index 0000000000..8c6cb85712
--- /dev/null
+++ b/internal/auth/repository/eventsourcing/handler/org_project_mapping.go
@@ -0,0 +1,113 @@
+package handler
+
+import (
+ "github.com/caos/logging"
+
+ "github.com/caos/zitadel/internal/eventstore/v1"
+ es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
+ "github.com/caos/zitadel/internal/eventstore/v1/query"
+ "github.com/caos/zitadel/internal/eventstore/v1/spooler"
+ "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
+ proj_view "github.com/caos/zitadel/internal/project/repository/view"
+ view_model "github.com/caos/zitadel/internal/project/repository/view/model"
+)
+
+const (
+ orgProjectMappingTable = "auth.org_project_mapping"
+)
+
+type OrgProjectMapping struct {
+ handler
+ subscription *v1.Subscription
+}
+
+func newOrgProjectMapping(
+ handler handler,
+) *OrgProjectMapping {
+ h := &OrgProjectMapping{
+ handler: handler,
+ }
+
+ h.subscribe()
+
+ return h
+}
+
+func (k *OrgProjectMapping) subscribe() {
+ k.subscription = k.es.Subscribe(k.AggregateTypes()...)
+ go func() {
+ for event := range k.subscription.Events {
+ query.ReduceEvent(k, event)
+ }
+ }()
+}
+
+func (p *OrgProjectMapping) ViewModel() string {
+ return orgProjectMappingTable
+}
+
+func (p *OrgProjectMapping) Subscription() *v1.Subscription {
+ return p.subscription
+}
+
+func (_ *OrgProjectMapping) AggregateTypes() []es_models.AggregateType {
+ return []es_models.AggregateType{model.ProjectAggregate}
+}
+
+func (p *OrgProjectMapping) CurrentSequence() (uint64, error) {
+ sequence, err := p.view.GetLatestOrgProjectMappingSequence()
+ if err != nil {
+ return 0, err
+ }
+ return sequence.CurrentSequence, nil
+}
+
+func (p *OrgProjectMapping) EventQuery() (*es_models.SearchQuery, error) {
+ sequence, err := p.view.GetLatestOrgProjectMappingSequence()
+ if err != nil {
+ return nil, err
+ }
+ return proj_view.ProjectQuery(sequence.CurrentSequence), nil
+}
+
+func (p *OrgProjectMapping) Reduce(event *es_models.Event) (err error) {
+ mapping := new(view_model.OrgProjectMapping)
+ switch event.Type {
+ case model.ProjectAdded:
+ mapping.OrgID = event.ResourceOwner
+ mapping.ProjectID = event.AggregateID
+ case model.ProjectRemoved:
+ err := p.view.DeleteOrgProjectMappingsByProjectID(event.AggregateID)
+ if err == nil {
+ return p.view.ProcessedOrgProjectMappingSequence(event)
+ }
+ case model.ProjectGrantAdded:
+ projectGrant := new(view_model.ProjectGrant)
+ projectGrant.SetData(event)
+ mapping.OrgID = projectGrant.GrantedOrgID
+ mapping.ProjectID = event.AggregateID
+ mapping.ProjectGrantID = projectGrant.GrantID
+ case model.ProjectGrantRemoved:
+ projectGrant := new(view_model.ProjectGrant)
+ projectGrant.SetData(event)
+ err := p.view.DeleteOrgProjectMappingsByProjectGrantID(event.AggregateID)
+ if err == nil {
+ return p.view.ProcessedOrgProjectMappingSequence(event)
+ }
+ default:
+ return p.view.ProcessedOrgProjectMappingSequence(event)
+ }
+ if err != nil {
+ return err
+ }
+ return p.view.PutOrgProjectMapping(mapping, event)
+}
+
+func (p *OrgProjectMapping) OnError(event *es_models.Event, err error) error {
+ logging.LogWithFields("SPOOL-2k0fS", "id", event.AggregateID).WithError(err).Warn("something went wrong in org project mapping handler")
+ return spooler.HandleError(event, err, p.view.GetLatestOrgProjectMappingFailedEvent, p.view.ProcessedOrgProjectMappingFailedEvent, p.view.ProcessedOrgProjectMappingSequence, p.errorCountUntilSkip)
+}
+
+func (p *OrgProjectMapping) OnSuccess() error {
+ return spooler.HandleSuccess(p.view.UpdateOrgProjectMappingSpoolerRunTimestamp)
+}
diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go
index 7d4523951d..6df9f147fc 100644
--- a/internal/auth/repository/eventsourcing/repository.go
+++ b/internal/auth/repository/eventsourcing/repository.go
@@ -111,6 +111,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
LoginPolicyViewProvider: view,
LockoutPolicyViewProvider: view,
UserGrantProvider: view,
+ ProjectProvider: view,
IdGenerator: idGenerator,
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
ExternalLoginCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
diff --git a/internal/auth/repository/eventsourcing/view/org_project_mapping.go b/internal/auth/repository/eventsourcing/view/org_project_mapping.go
new file mode 100644
index 0000000000..8103cf8c5c
--- /dev/null
+++ b/internal/auth/repository/eventsourcing/view/org_project_mapping.go
@@ -0,0 +1,61 @@
+package view
+
+import (
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore/v1/models"
+ "github.com/caos/zitadel/internal/project/repository/view"
+ "github.com/caos/zitadel/internal/project/repository/view/model"
+ "github.com/caos/zitadel/internal/view/repository"
+)
+
+const (
+ orgPrgojectMappingTable = "auth.org_project_mapping"
+)
+
+func (v *View) OrgProjectMappingByIDs(orgID, projectID string) (*model.OrgProjectMapping, error) {
+ return view.OrgProjectMappingByIDs(v.Db, orgPrgojectMappingTable, orgID, projectID)
+}
+
+func (v *View) PutOrgProjectMapping(mapping *model.OrgProjectMapping, event *models.Event) error {
+ err := view.PutOrgProjectMapping(v.Db, orgPrgojectMappingTable, mapping)
+ if err != nil {
+ return err
+ }
+ return v.ProcessedOrgProjectMappingSequence(event)
+}
+
+func (v *View) DeleteOrgProjectMapping(orgID, projectID string, event *models.Event) error {
+ err := view.DeleteOrgProjectMapping(v.Db, orgPrgojectMappingTable, orgID, projectID)
+ if err != nil && !errors.IsNotFound(err) {
+ return err
+ }
+ return v.ProcessedOrgProjectMappingSequence(event)
+}
+
+func (v *View) DeleteOrgProjectMappingsByProjectID(projectID string) error {
+ return view.DeleteOrgProjectMappingsByProjectID(v.Db, orgPrgojectMappingTable, projectID)
+}
+
+func (v *View) DeleteOrgProjectMappingsByProjectGrantID(projectGrantID string) error {
+ return view.DeleteOrgProjectMappingsByProjectGrantID(v.Db, orgPrgojectMappingTable, projectGrantID)
+}
+
+func (v *View) GetLatestOrgProjectMappingSequence() (*repository.CurrentSequence, error) {
+ return v.latestSequence(orgPrgojectMappingTable)
+}
+
+func (v *View) ProcessedOrgProjectMappingSequence(event *models.Event) error {
+ return v.saveCurrentSequence(orgPrgojectMappingTable, event)
+}
+
+func (v *View) UpdateOrgProjectMappingSpoolerRunTimestamp() error {
+ return v.updateSpoolerRunSequence(orgPrgojectMappingTable)
+}
+
+func (v *View) GetLatestOrgProjectMappingFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
+ return v.latestFailedEvent(orgPrgojectMappingTable, sequence)
+}
+
+func (v *View) ProcessedOrgProjectMappingFailedEvent(failedEvent *repository.FailedEvent) error {
+ return v.saveFailedEvent(failedEvent)
+}
diff --git a/internal/command/project.go b/internal/command/project.go
index b5f3bbdd15..2257d9feb1 100644
--- a/internal/command/project.go
+++ b/internal/command/project.go
@@ -45,7 +45,7 @@ func (c *Commands) addProject(ctx context.Context, projectAdd *domain.Project, r
projectRole = domain.RoleProjectOwnerGlobal
}
events := []eventstore.EventPusher{
- project.NewProjectAddedEvent(ctx, projectAgg, projectAdd.Name),
+ project.NewProjectAddedEvent(ctx, projectAgg, projectAdd.Name, projectAdd.ProjectRoleAssertion, projectAdd.ProjectRoleCheck, projectAdd.HasProjectCheck),
project.NewProjectMemberAddedEvent(ctx, projectAgg, ownerUserID, projectRole),
}
return events, addedProject, nil
@@ -87,7 +87,13 @@ func (c *Commands) ChangeProject(ctx context.Context, projectChange *domain.Proj
}
projectAgg := ProjectAggregateFromWriteModel(&existingProject.WriteModel)
- changedEvent, hasChanged, err := existingProject.NewChangedEvent(ctx, projectAgg, projectChange.Name, projectChange.ProjectRoleAssertion, projectChange.ProjectRoleCheck)
+ changedEvent, hasChanged, err := existingProject.NewChangedEvent(
+ ctx,
+ projectAgg,
+ projectChange.Name,
+ projectChange.ProjectRoleAssertion,
+ projectChange.ProjectRoleCheck,
+ projectChange.HasProjectCheck)
if err != nil {
return nil, err
}
diff --git a/internal/command/project_application_api_test.go b/internal/command/project_application_api_test.go
index a438b4f285..507f0fe249 100644
--- a/internal/command/project_application_api_test.go
+++ b/internal/command/project_application_api_test.go
@@ -84,7 +84,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
),
@@ -113,7 +113,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
@@ -180,7 +180,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
diff --git a/internal/command/project_application_oidc_test.go b/internal/command/project_application_oidc_test.go
index b590cf6cb4..ecfef9ebb0 100644
--- a/internal/command/project_application_oidc_test.go
+++ b/internal/command/project_application_oidc_test.go
@@ -87,7 +87,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
),
@@ -116,7 +116,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
diff --git a/internal/command/project_converter.go b/internal/command/project_converter.go
index 2aee9bb5ce..4af0418c1a 100644
--- a/internal/command/project_converter.go
+++ b/internal/command/project_converter.go
@@ -10,6 +10,7 @@ func projectWriteModelToProject(writeModel *ProjectWriteModel) *domain.Project {
Name: writeModel.Name,
ProjectRoleAssertion: writeModel.ProjectRoleAssertion,
ProjectRoleCheck: writeModel.ProjectRoleCheck,
+ HasProjectCheck: writeModel.HasProjectCheck,
}
}
diff --git a/internal/command/project_grant_test.go b/internal/command/project_grant_test.go
index 4274622b52..15f28a2cc9 100644
--- a/internal/command/project_grant_test.go
+++ b/internal/command/project_grant_test.go
@@ -87,7 +87,7 @@ func TestCommandSide_AddProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -116,7 +116,7 @@ func TestCommandSide_AddProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -152,7 +152,7 @@ func TestCommandSide_AddProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -341,7 +341,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -379,7 +379,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -424,7 +424,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -477,7 +477,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -556,7 +556,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -637,7 +637,7 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -813,7 +813,7 @@ func TestCommandSide_DeactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -839,7 +839,7 @@ func TestCommandSide_DeactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -876,7 +876,7 @@ func TestCommandSide_DeactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1009,7 +1009,7 @@ func TestCommandSide_ReactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1035,7 +1035,7 @@ func TestCommandSide_ReactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1068,7 +1068,7 @@ func TestCommandSide_ReactivateProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1206,7 +1206,7 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1232,7 +1232,7 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1277,7 +1277,7 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -1324,7 +1324,7 @@ func TestCommandSide_RemoveProjectGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
diff --git a/internal/command/project_model.go b/internal/command/project_model.go
index 9e5cc439c6..8655f3d9b8 100644
--- a/internal/command/project_model.go
+++ b/internal/command/project_model.go
@@ -14,6 +14,7 @@ type ProjectWriteModel struct {
Name string
ProjectRoleAssertion bool
ProjectRoleCheck bool
+ HasProjectCheck bool
State domain.ProjectState
}
@@ -33,6 +34,7 @@ func (wm *ProjectWriteModel) Reduce() error {
wm.Name = e.Name
wm.ProjectRoleAssertion = e.ProjectRoleAssertion
wm.ProjectRoleCheck = e.ProjectRoleCheck
+ wm.HasProjectCheck = e.HasProjectCheck
wm.State = domain.ProjectStateActive
case *project.ProjectChangeEvent:
if e.Name != nil {
@@ -44,6 +46,9 @@ func (wm *ProjectWriteModel) Reduce() error {
if e.ProjectRoleCheck != nil {
wm.ProjectRoleCheck = *e.ProjectRoleCheck
}
+ if e.HasProjectCheck != nil {
+ wm.HasProjectCheck = *e.HasProjectCheck
+ }
case *project.ProjectDeactivatedEvent:
if wm.State == domain.ProjectStateRemoved {
continue
@@ -80,7 +85,8 @@ func (wm *ProjectWriteModel) NewChangedEvent(
aggregate *eventstore.Aggregate,
name string,
projectRoleAssertion,
- projectRoleCheck bool,
+ projectRoleCheck,
+ hasProjectCheck bool,
) (*project.ProjectChangeEvent, bool, error) {
changes := make([]project.ProjectChanges, 0)
var err error
@@ -96,6 +102,9 @@ func (wm *ProjectWriteModel) NewChangedEvent(
if wm.ProjectRoleCheck != projectRoleCheck {
changes = append(changes, project.ChangeProjectRoleCheck(projectRoleCheck))
}
+ if wm.HasProjectCheck != hasProjectCheck {
+ changes = append(changes, project.ChangeHasProjectCheck(hasProjectCheck))
+ }
if len(changes) == 0 {
return nil, false, nil
}
diff --git a/internal/command/project_role_test.go b/internal/command/project_role_test.go
index a4bebdd1ea..80a13c8577 100644
--- a/internal/command/project_role_test.go
+++ b/internal/command/project_role_test.go
@@ -41,7 +41,7 @@ func TestCommandSide_AddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -76,7 +76,7 @@ func TestCommandSide_AddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -104,7 +104,7 @@ func TestCommandSide_AddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -148,7 +148,7 @@ func TestCommandSide_AddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -240,7 +240,7 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -277,7 +277,7 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -309,7 +309,7 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -367,7 +367,7 @@ func TestCommandSide_BulkAddProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -486,7 +486,7 @@ func TestCommandSide_ChangeProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -521,7 +521,7 @@ func TestCommandSide_ChangeProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -568,7 +568,7 @@ func TestCommandSide_ChangeProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -609,7 +609,7 @@ func TestCommandSide_ChangeProjectRole(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
diff --git a/internal/command/project_test.go b/internal/command/project_test.go
index bfb1afeb55..63d99d8251 100644
--- a/internal/command/project_test.go
+++ b/internal/command/project_test.go
@@ -2,6 +2,10 @@ package command
import (
"context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@@ -12,8 +16,6 @@ import (
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/member"
"github.com/caos/zitadel/internal/repository/project"
- "github.com/stretchr/testify/assert"
- "testing"
)
func TestCommandSide_AddProject(t *testing.T) {
@@ -71,7 +73,7 @@ func TestCommandSide_AddProject(t *testing.T) {
eventFromEventPusher(project.NewProjectAddedEvent(
context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project",
+ "project", true, true, true,
),
),
eventFromEventPusher(project.NewProjectMemberAddedEvent(
@@ -91,7 +93,10 @@ func TestCommandSide_AddProject(t *testing.T) {
args: args{
ctx: context.Background(),
project: &domain.Project{
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
resourceOwner: "org1",
ownerID: "user1",
@@ -118,7 +123,7 @@ func TestCommandSide_AddProject(t *testing.T) {
eventFromEventPusher(project.NewProjectAddedEvent(
context.Background(),
&project.NewAggregate("project1", "globalorg").Aggregate,
- "project",
+ "project", true, true, true,
),
),
eventFromEventPusher(project.NewProjectMemberAddedEvent(
@@ -138,7 +143,10 @@ func TestCommandSide_AddProject(t *testing.T) {
args: args{
ctx: context.Background(),
project: &domain.Project{
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
resourceOwner: "globalorg",
ownerID: "user1",
@@ -149,7 +157,10 @@ func TestCommandSide_AddProject(t *testing.T) {
ResourceOwner: "globalorg",
AggregateID: "project1",
},
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
},
},
@@ -171,7 +182,7 @@ func TestCommandSide_AddProject(t *testing.T) {
eventFromEventPusher(project.NewProjectAddedEvent(
context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project",
+ "project", true, true, true,
),
),
eventFromEventPusher(project.NewProjectMemberAddedEvent(
@@ -191,7 +202,10 @@ func TestCommandSide_AddProject(t *testing.T) {
args: args{
ctx: context.Background(),
project: &domain.Project{
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
resourceOwner: "org1",
ownerID: "user1",
@@ -202,7 +216,10 @@ func TestCommandSide_AddProject(t *testing.T) {
ResourceOwner: "org1",
AggregateID: "project1",
},
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
},
},
@@ -315,7 +332,7 @@ func TestCommandSide_ChangeProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectRemovedEvent(context.Background(),
@@ -348,7 +365,7 @@ func TestCommandSide_ChangeProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
),
@@ -359,7 +376,10 @@ func TestCommandSide_ChangeProject(t *testing.T) {
ObjectRoot: models.ObjectRoot{
AggregateID: "project1",
},
- Name: "project",
+ Name: "project",
+ ProjectRoleAssertion: true,
+ ProjectRoleCheck: true,
+ HasProjectCheck: true,
},
resourceOwner: "org1",
},
@@ -376,7 +396,7 @@ func TestCommandSide_ChangeProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
@@ -387,8 +407,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
"org1",
"project",
"project-new",
- true,
- true),
+ false,
+ false,
+ false),
),
},
uniqueConstraintsFromEventConstraint(project.NewRemoveProjectNameUniqueConstraint("project", "org1")),
@@ -403,8 +424,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
AggregateID: "project1",
},
Name: "project-new",
- ProjectRoleAssertion: true,
- ProjectRoleCheck: true,
+ ProjectRoleAssertion: false,
+ ProjectRoleCheck: false,
+ HasProjectCheck: false,
},
resourceOwner: "org1",
},
@@ -415,8 +437,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
ResourceOwner: "org1",
},
Name: "project-new",
- ProjectRoleAssertion: true,
- ProjectRoleCheck: true,
+ ProjectRoleAssertion: false,
+ ProjectRoleCheck: false,
+ HasProjectCheck: false,
},
},
},
@@ -429,7 +452,7 @@ func TestCommandSide_ChangeProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
@@ -440,8 +463,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
"org1",
"project",
"",
- true,
- true),
+ false,
+ false,
+ false),
),
},
),
@@ -454,8 +478,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
AggregateID: "project1",
},
Name: "project",
- ProjectRoleAssertion: true,
- ProjectRoleCheck: true,
+ ProjectRoleAssertion: false,
+ ProjectRoleCheck: false,
+ HasProjectCheck: false,
},
resourceOwner: "org1",
},
@@ -466,8 +491,9 @@ func TestCommandSide_ChangeProject(t *testing.T) {
ResourceOwner: "org1",
},
Name: "project",
- ProjectRoleAssertion: true,
- ProjectRoleCheck: true,
+ ProjectRoleAssertion: false,
+ ProjectRoleCheck: false,
+ HasProjectCheck: false,
},
},
},
@@ -568,7 +594,7 @@ func TestCommandSide_DeactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectRemovedEvent(context.Background(),
@@ -596,7 +622,7 @@ func TestCommandSide_DeactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectDeactivatedEvent(context.Background(),
@@ -623,7 +649,7 @@ func TestCommandSide_DeactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
@@ -744,7 +770,7 @@ func TestCommandSide_ReactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectRemovedEvent(context.Background(),
@@ -772,7 +798,7 @@ func TestCommandSide_ReactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
),
@@ -795,7 +821,7 @@ func TestCommandSide_ReactivateProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectDeactivatedEvent(context.Background(),
@@ -920,7 +946,7 @@ func TestCommandSide_RemoveProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
eventFromEventPusher(
project.NewProjectRemovedEvent(context.Background(),
@@ -948,7 +974,7 @@ func TestCommandSide_RemoveProject(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "project"),
+ "project", true, true, true),
),
),
expectPush(
@@ -994,10 +1020,11 @@ func TestCommandSide_RemoveProject(t *testing.T) {
}
}
-func newProjectChangedEvent(ctx context.Context, projectID, resourceOwner, oldName, newName string, roleAssertion, roleCheck bool) *project.ProjectChangeEvent {
+func newProjectChangedEvent(ctx context.Context, projectID, resourceOwner, oldName, newName string, roleAssertion, roleCheck, hasProjectCheck bool) *project.ProjectChangeEvent {
changes := []project.ProjectChanges{
project.ChangeProjectRoleAssertion(roleAssertion),
project.ChangeProjectRoleCheck(roleCheck),
+ project.ChangeHasProjectCheck(hasProjectCheck),
}
if newName != "" {
changes = append(changes, project.ChangeName(newName))
diff --git a/internal/command/user_grant_test.go b/internal/command/user_grant_test.go
index 5a8f57e11d..a3ce1a7a2b 100644
--- a/internal/command/user_grant_test.go
+++ b/internal/command/user_grant_test.go
@@ -140,7 +140,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -187,7 +187,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -229,7 +229,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -272,7 +272,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -331,7 +331,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -403,7 +403,7 @@ func TestCommandSide_AddUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -717,7 +717,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -776,7 +776,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -830,7 +830,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
),
@@ -885,7 +885,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -956,7 +956,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
@@ -1043,7 +1043,7 @@ func TestCommandSide_ChangeUserGrant(t *testing.T) {
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
- "projectname1",
+ "projectname1", true, true, true,
),
),
eventFromEventPusher(
diff --git a/internal/domain/next_step.go b/internal/domain/next_step.go
index 1d5c201f0d..57eaa3304b 100644
--- a/internal/domain/next_step.go
+++ b/internal/domain/next_step.go
@@ -26,6 +26,7 @@ const (
NextStepPasswordless
NextStepPasswordlessRegistrationPrompt
NextStepRegistration
+ NextStepProjectRequired
)
type LoginStep struct{}
@@ -161,6 +162,12 @@ func (s *GrantRequiredStep) Type() NextStepType {
return NextStepGrantRequired
}
+type ProjectRequiredStep struct{}
+
+func (s *ProjectRequiredStep) Type() NextStepType {
+ return NextStepProjectRequired
+}
+
type RedirectToCallbackStep struct{}
func (s *RedirectToCallbackStep) Type() NextStepType {
diff --git a/internal/domain/project.go b/internal/domain/project.go
index ec83818f5e..fb8262ebbc 100644
--- a/internal/domain/project.go
+++ b/internal/domain/project.go
@@ -11,6 +11,7 @@ type Project struct {
Name string
ProjectRoleAssertion bool
ProjectRoleCheck bool
+ HasProjectCheck bool
}
type ProjectState int32
diff --git a/internal/management/repository/eventsourcing/handler/application.go b/internal/management/repository/eventsourcing/handler/application.go
index c7be7ecffe..dd45a94586 100644
--- a/internal/management/repository/eventsourcing/handler/application.go
+++ b/internal/management/repository/eventsourcing/handler/application.go
@@ -84,6 +84,7 @@ func (a *Application) Reduce(event *models.Event) (err error) {
return err
}
app.ProjectRoleCheck = project.ProjectRoleCheck
+ app.HasProjectCheck = project.HasProjectCheck
app.ProjectRoleAssertion = project.ProjectRoleAssertion
err = app.AppendEvent(event)
diff --git a/internal/project/model/org_project_mapping_view.go b/internal/project/model/org_project_mapping_view.go
new file mode 100644
index 0000000000..3b71510666
--- /dev/null
+++ b/internal/project/model/org_project_mapping_view.go
@@ -0,0 +1,53 @@
+package model
+
+import (
+ "github.com/caos/zitadel/internal/domain"
+
+ "time"
+)
+
+type OrgProjectMapping struct {
+ OrgID string
+ ProjectID string
+}
+
+type OrgProjectMappingViewSearchRequest struct {
+ Offset uint64
+ Limit uint64
+ SortingColumn OrgProjectMappingViewSearchKey
+ Asc bool
+ Queries []*OrgProjectMappingViewSearchQuery
+}
+
+type OrgProjectMappingViewSearchKey int32
+
+const (
+ OrgProjectMappingSearchKeyUnspecified OrgProjectMappingViewSearchKey = iota
+ OrgProjectMappingSearchKeyProjectID
+ OrgProjectMappingSearchKeyOrgID
+ OrgProjectMappingSearchKeyProjectGrantID
+)
+
+type OrgProjectMappingViewSearchQuery struct {
+ Key OrgProjectMappingViewSearchKey
+ Method domain.SearchMethod
+ Value interface{}
+}
+
+type OrgProjectMappingViewSearchResponse struct {
+ Offset uint64
+ Limit uint64
+ TotalResult uint64
+ Result []*OrgProjectMapping
+ Sequence uint64
+ Timestamp time.Time
+}
+
+func (r *OrgProjectMappingViewSearchRequest) GetSearchQuery(key OrgProjectMappingViewSearchKey) (int, *OrgProjectMappingViewSearchQuery) {
+ for i, q := range r.Queries {
+ if q.Key == key {
+ return i, q
+ }
+ }
+ return -1, nil
+}
diff --git a/internal/project/model/project.go b/internal/project/model/project.go
index 8dd899300f..3a6e3e95f9 100644
--- a/internal/project/model/project.go
+++ b/internal/project/model/project.go
@@ -17,6 +17,7 @@ type Project struct {
Grants []*ProjectGrant
ProjectRoleAssertion bool
ProjectRoleCheck bool
+ HasProjectCheck bool
}
type ProjectChanges struct {
Changes []*ProjectChange
diff --git a/internal/project/model/project_view.go b/internal/project/model/project_view.go
index 87273bb56b..e1f2f52360 100644
--- a/internal/project/model/project_view.go
+++ b/internal/project/model/project_view.go
@@ -16,6 +16,7 @@ type ProjectView struct {
ResourceOwner string
ProjectRoleAssertion bool
ProjectRoleCheck bool
+ HasProjectCheck bool
Sequence uint64
}
diff --git a/internal/project/repository/eventsourcing/model/project.go b/internal/project/repository/eventsourcing/model/project.go
index 6eb1b6bb2e..80f8e611fd 100644
--- a/internal/project/repository/eventsourcing/model/project.go
+++ b/internal/project/repository/eventsourcing/model/project.go
@@ -18,6 +18,7 @@ type Project struct {
Name string `json:"name,omitempty"`
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
+ HasProjectCheck bool `json:"hasProjectCheck,omitempty"`
State int32 `json:"-"`
Members []*ProjectMember `json:"-"`
Roles []*ProjectRole `json:"-"`
@@ -25,47 +26,6 @@ type Project struct {
Grants []*ProjectGrant `json:"-"`
}
-func GetProject(projects []*Project, id string) (int, *Project) {
- for i, p := range projects {
- if p.AggregateID == id {
- return i, p
- }
- }
- return -1, nil
-}
-
-func (p *Project) Changes(changed *Project) map[string]interface{} {
- changes := make(map[string]interface{}, 1)
- if changed.Name != "" && p.Name != changed.Name {
- changes["name"] = changed.Name
- }
- if p.ProjectRoleAssertion != changed.ProjectRoleAssertion {
- changes["projectRoleAssertion"] = changed.ProjectRoleAssertion
- }
- if p.ProjectRoleCheck != changed.ProjectRoleCheck {
- changes["projectRoleCheck"] = changed.ProjectRoleCheck
- }
- return changes
-}
-
-func ProjectFromModel(project *model.Project) *Project {
- members := ProjectMembersFromModel(project.Members)
- roles := ProjectRolesFromModel(project.Roles)
- apps := AppsFromModel(project.Applications)
- grants := GrantsFromModel(project.Grants)
- return &Project{
- ObjectRoot: project.ObjectRoot,
- Name: project.Name,
- ProjectRoleAssertion: project.ProjectRoleAssertion,
- ProjectRoleCheck: project.ProjectRoleCheck,
- State: int32(project.State),
- Members: members,
- Roles: roles,
- Applications: apps,
- Grants: grants,
- }
-}
-
func ProjectToModel(project *Project) *model.Project {
members := ProjectMembersToModel(project.Members)
roles := ProjectRolesToModel(project.Roles)
diff --git a/internal/project/repository/eventsourcing/model/project_test.go b/internal/project/repository/eventsourcing/model/project_test.go
index 5da6f8e9f9..6121d63665 100644
--- a/internal/project/repository/eventsourcing/model/project_test.go
+++ b/internal/project/repository/eventsourcing/model/project_test.go
@@ -8,50 +8,6 @@ import (
"github.com/caos/zitadel/internal/project/model"
)
-func TestProjectChanges(t *testing.T) {
- type args struct {
- existingProject *Project
- newProject *Project
- }
- type res struct {
- changesLen int
- }
- tests := []struct {
- name string
- args args
- res res
- }{
- {
- name: "project name changes",
- args: args{
- existingProject: &Project{Name: "Name"},
- newProject: &Project{Name: "NameChanged"},
- },
- res: res{
- changesLen: 1,
- },
- },
- {
- name: "no changes",
- args: args{
- existingProject: &Project{Name: "Name"},
- newProject: &Project{Name: "Name"},
- },
- res: res{
- changesLen: 0,
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- changes := tt.args.existingProject.Changes(tt.args.newProject)
- if len(changes) != tt.res.changesLen {
- t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
- }
- })
- }
-}
-
func TestProjectFromEvents(t *testing.T) {
type args struct {
event []*es_models.Event
diff --git a/internal/project/repository/view/model/application.go b/internal/project/repository/view/model/application.go
index 4440c10ec1..4ca3473ead 100644
--- a/internal/project/repository/view/model/application.go
+++ b/internal/project/repository/view/model/application.go
@@ -31,6 +31,7 @@ type ApplicationView struct {
State int32 `json:"-" gorm:"column:app_state"`
ProjectRoleAssertion bool `json:"projectRoleAssertion" gorm:"column:project_role_assertion"`
ProjectRoleCheck bool `json:"projectRoleCheck" gorm:"column:project_role_check"`
+ HasProjectCheck bool `json:"hasProjectCheck" gorm:"column:has_project_check"`
IsOIDC bool `json:"-" gorm:"column:is_oidc"`
OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"`
@@ -234,6 +235,7 @@ func (a *ApplicationView) setProjectChanges(event *models.Event) error {
changes := struct {
ProjectRoleAssertion *bool `json:"projectRoleAssertion,omitempty"`
ProjectRoleCheck *bool `json:"projectRoleCheck,omitempty"`
+ HasProjectCheck *bool `json:"hasProjectCheck,omitempty"`
}{}
if err := json.Unmarshal(event.Data, &changes); err != nil {
logging.Log("EVEN-DFbfg").WithError(err).Error("could not unmarshal event data")
@@ -245,5 +247,8 @@ func (a *ApplicationView) setProjectChanges(event *models.Event) error {
if changes.ProjectRoleCheck != nil {
a.ProjectRoleCheck = *changes.ProjectRoleCheck
}
+ if changes.HasProjectCheck != nil {
+ a.HasProjectCheck = *changes.HasProjectCheck
+ }
return nil
}
diff --git a/internal/project/repository/view/model/org_project_mapping.go b/internal/project/repository/view/model/org_project_mapping.go
new file mode 100644
index 0000000000..e2f5705d38
--- /dev/null
+++ b/internal/project/repository/view/model/org_project_mapping.go
@@ -0,0 +1,13 @@
+package model
+
+const (
+ OrgProjectMappingKeyProjectID = "project_id"
+ OrgProjectMappingKeyOrgID = "org_id"
+ OrgProjectMappingKeyProjectGrantID = "project_grant_id"
+)
+
+type OrgProjectMapping struct {
+ ProjectID string `json:"-" gorm:"column:project_id;primary_key"`
+ OrgID string `json:"-" gorm:"column:org_id;primary_key"`
+ ProjectGrantID string `json:"-" gorm:"column:project_grant_id;"`
+}
diff --git a/internal/project/repository/view/model/org_project_mapping_query.go b/internal/project/repository/view/model/org_project_mapping_query.go
new file mode 100644
index 0000000000..cc638fcb5c
--- /dev/null
+++ b/internal/project/repository/view/model/org_project_mapping_query.go
@@ -0,0 +1,63 @@
+package model
+
+import (
+ "github.com/caos/zitadel/internal/domain"
+ proj_model "github.com/caos/zitadel/internal/project/model"
+ "github.com/caos/zitadel/internal/view/repository"
+)
+
+type OrgProjectMappingSearchRequest proj_model.OrgProjectMappingViewSearchRequest
+type OrgProjectMappingSearchQuery proj_model.OrgProjectMappingViewSearchQuery
+type OrgProjectMappingSearchKey proj_model.OrgProjectMappingViewSearchKey
+
+func (req OrgProjectMappingSearchRequest) GetLimit() uint64 {
+ return req.Limit
+}
+
+func (req OrgProjectMappingSearchRequest) GetOffset() uint64 {
+ return req.Offset
+}
+
+func (req OrgProjectMappingSearchRequest) GetSortingColumn() repository.ColumnKey {
+ if req.SortingColumn == proj_model.OrgProjectMappingSearchKeyUnspecified {
+ return nil
+ }
+ return OrgProjectMappingSearchKey(req.SortingColumn)
+}
+
+func (req OrgProjectMappingSearchRequest) GetAsc() bool {
+ return req.Asc
+}
+
+func (req OrgProjectMappingSearchRequest) GetQueries() []repository.SearchQuery {
+ result := make([]repository.SearchQuery, len(req.Queries))
+ for i, q := range req.Queries {
+ result[i] = OrgProjectMappingSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
+ }
+ return result
+}
+
+func (req OrgProjectMappingSearchQuery) GetKey() repository.ColumnKey {
+ return OrgProjectMappingSearchKey(req.Key)
+}
+
+func (req OrgProjectMappingSearchQuery) GetMethod() domain.SearchMethod {
+ return req.Method
+}
+
+func (req OrgProjectMappingSearchQuery) GetValue() interface{} {
+ return req.Value
+}
+
+func (key OrgProjectMappingSearchKey) ToColumnName() string {
+ switch proj_model.OrgProjectMappingViewSearchKey(key) {
+ case proj_model.OrgProjectMappingSearchKeyOrgID:
+ return OrgProjectMappingKeyOrgID
+ case proj_model.OrgProjectMappingSearchKeyProjectID:
+ return OrgProjectMappingKeyProjectID
+ case proj_model.OrgProjectMappingSearchKeyProjectGrantID:
+ return OrgProjectMappingKeyProjectGrantID
+ default:
+ return ""
+ }
+}
diff --git a/internal/project/repository/view/model/project.go b/internal/project/repository/view/model/project.go
index 0ec8c84665..81a62c7026 100644
--- a/internal/project/repository/view/model/project.go
+++ b/internal/project/repository/view/model/project.go
@@ -27,23 +27,10 @@ type ProjectView struct {
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
ProjectRoleAssertion bool `json:"projectRoleAssertion" gorm:"column:project_role_assertion"`
ProjectRoleCheck bool `json:"projectRoleCheck" gorm:"column:project_role_check"`
+ HasProjectCheck bool `json:"hasProjectCheck" gorm:"column:has_project_check"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
-func ProjectFromModel(project *model.ProjectView) *ProjectView {
- return &ProjectView{
- ProjectID: project.ProjectID,
- Name: project.Name,
- ChangeDate: project.ChangeDate,
- CreationDate: project.CreationDate,
- State: int32(project.State),
- ResourceOwner: project.ResourceOwner,
- ProjectRoleAssertion: project.ProjectRoleAssertion,
- ProjectRoleCheck: project.ProjectRoleCheck,
- Sequence: project.Sequence,
- }
-}
-
func ProjectToModel(project *ProjectView) *model.ProjectView {
return &model.ProjectView{
ProjectID: project.ProjectID,
@@ -54,6 +41,7 @@ func ProjectToModel(project *ProjectView) *model.ProjectView {
ResourceOwner: project.ResourceOwner,
ProjectRoleAssertion: project.ProjectRoleAssertion,
ProjectRoleCheck: project.ProjectRoleCheck,
+ HasProjectCheck: project.HasProjectCheck,
Sequence: project.Sequence,
}
}
diff --git a/internal/project/repository/view/org_project_mapping_view.go b/internal/project/repository/view/org_project_mapping_view.go
new file mode 100644
index 0000000000..ce437daf0e
--- /dev/null
+++ b/internal/project/repository/view/org_project_mapping_view.go
@@ -0,0 +1,51 @@
+package view
+
+import (
+ "github.com/jinzhu/gorm"
+
+ "github.com/caos/zitadel/internal/domain"
+ caos_errs "github.com/caos/zitadel/internal/errors"
+ proj_model "github.com/caos/zitadel/internal/project/model"
+ "github.com/caos/zitadel/internal/project/repository/view/model"
+ "github.com/caos/zitadel/internal/view/repository"
+)
+
+func OrgProjectMappingByIDs(db *gorm.DB, table, orgID, projectID string) (*model.OrgProjectMapping, error) {
+ orgProjectMapping := new(model.OrgProjectMapping)
+
+ projectIDQuery := model.OrgProjectMappingSearchQuery{Key: proj_model.OrgProjectMappingSearchKeyProjectID, Value: projectID, Method: domain.SearchMethodEquals}
+ orgIDQuery := model.OrgProjectMappingSearchQuery{Key: proj_model.OrgProjectMappingSearchKeyOrgID, Value: orgID, Method: domain.SearchMethodEquals}
+ query := repository.PrepareGetByQuery(table, projectIDQuery, orgIDQuery)
+ err := query(db, orgProjectMapping)
+ if caos_errs.IsNotFound(err) {
+ return nil, caos_errs.ThrowNotFound(nil, "VIEW-fn9fs", "Errors.OrgProjectMapping.NotExisting")
+ }
+ return orgProjectMapping, err
+}
+
+func PutOrgProjectMapping(db *gorm.DB, table string, grant *model.OrgProjectMapping) error {
+ save := repository.PrepareSave(table)
+ return save(db, grant)
+}
+
+func DeleteOrgProjectMapping(db *gorm.DB, table, orgID, projectID string) error {
+ projectIDSearch := repository.Key{Key: model.OrgProjectMappingSearchKey(proj_model.OrgProjectMappingSearchKeyProjectID), Value: projectID}
+ orgIDSearch := repository.Key{Key: model.OrgProjectMappingSearchKey(proj_model.OrgProjectMappingSearchKeyOrgID), Value: orgID}
+ delete := repository.PrepareDeleteByKeys(table, projectIDSearch, orgIDSearch)
+ return delete(db)
+}
+
+func DeleteOrgProjectMappingsByProjectID(db *gorm.DB, table, projectID string) error {
+ delete := repository.PrepareDeleteByKey(table, model.OrgProjectMappingSearchKey(proj_model.OrgProjectMappingSearchKeyProjectID), projectID)
+ return delete(db)
+}
+
+func DeleteOrgProjectMappingsByProjectGrantID(db *gorm.DB, table, projectGrantID string) error {
+ delete := repository.PrepareDeleteByKey(table, model.OrgProjectMappingSearchKey(proj_model.OrgProjectMappingSearchKeyProjectGrantID), projectGrantID)
+ return delete(db)
+}
+
+func DeleteOrgProjectMappingsByOrgID(db *gorm.DB, table, orgID string) error {
+ delete := repository.PrepareDeleteByKey(table, model.OrgProjectMappingSearchKey(proj_model.OrgProjectMappingSearchKeyOrgID), orgID)
+ return delete(db)
+}
diff --git a/internal/repository/project/project.go b/internal/repository/project/project.go
index faba643146..c63c0d06e9 100644
--- a/internal/repository/project/project.go
+++ b/internal/repository/project/project.go
@@ -38,6 +38,7 @@ type ProjectAddedEvent struct {
Name string `json:"name,omitempty"`
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
+ HasProjectCheck bool `json:"hasProjectCheck,omitempty"`
}
func (e *ProjectAddedEvent) Data() interface{} {
@@ -52,6 +53,9 @@ func NewProjectAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
name string,
+ projectRoleAssertion,
+ projectRoleCheck,
+ hasProjectCheck bool,
) *ProjectAddedEvent {
return &ProjectAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
@@ -59,7 +63,10 @@ func NewProjectAddedEvent(
aggregate,
ProjectAddedType,
),
- Name: name,
+ Name: name,
+ ProjectRoleAssertion: projectRoleAssertion,
+ ProjectRoleCheck: projectRoleCheck,
+ HasProjectCheck: hasProjectCheck,
}
}
@@ -82,6 +89,7 @@ type ProjectChangeEvent struct {
Name *string `json:"name,omitempty"`
ProjectRoleAssertion *bool `json:"projectRoleAssertion,omitempty"`
ProjectRoleCheck *bool `json:"projectRoleCheck,omitempty"`
+ HasProjectCheck *bool `json:"hasProjectCheck,omitempty"`
oldName string
}
@@ -142,6 +150,12 @@ func ChangeProjectRoleCheck(projectRoleCheck bool) func(event *ProjectChangeEven
}
}
+func ChangeHasProjectCheck(ChangeHasProjectCheck bool) func(event *ProjectChangeEvent) {
+ return func(e *ProjectChangeEvent) {
+ e.HasProjectCheck = &ChangeHasProjectCheck
+ }
+}
+
func ProjectChangeEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &ProjectChangeEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
diff --git a/internal/ui/login/handler/renderer.go b/internal/ui/login/handler/renderer.go
index a715358967..4d3b1d9cbf 100644
--- a/internal/ui/login/handler/renderer.go
+++ b/internal/ui/login/handler/renderer.go
@@ -291,6 +291,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID)
case *domain.GrantRequiredStep:
l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
+ case *domain.ProjectRequiredStep:
+ l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-m92d", "Errors.User.ProjectRequired"))
default:
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
}
diff --git a/internal/ui/login/static/i18n/de.yaml b/internal/ui/login/static/i18n/de.yaml
index 0420038a61..c62bd91a31 100644
--- a/internal/ui/login/static/i18n/de.yaml
+++ b/internal/ui/login/static/i18n/de.yaml
@@ -349,6 +349,7 @@ Errors:
ExternalUserIDEmpty: Externe User ID ist leer
UserDisplayNameEmpty: Benutzer Anzeige Name ist leer
GrantRequired: Der Login an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator.
+ ProjectRequired: Der Login an diese Applikation ist nicht möglich. Die Organisation des Benutzer benötigt Berechtigung auf das Projekt. Bitte melde dich bei deinem Administrator.
IdentityProvider:
InvalidConfig: Identitäts Provider Konfiguration ist ungültig
IAM:
diff --git a/internal/ui/login/static/i18n/en.yaml b/internal/ui/login/static/i18n/en.yaml
index c5e4070bcc..6a3b4256a7 100644
--- a/internal/ui/login/static/i18n/en.yaml
+++ b/internal/ui/login/static/i18n/en.yaml
@@ -350,6 +350,7 @@ Errors:
ExternalUserIDEmpty: External User ID is empty
UserDisplayNameEmpty: User Display Name is empty
GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator.
+ ProjectRequired: Login not possible. The organisation of the user must be granted to the project. Please contact your administrator.
IdentityProvider:
InvalidConfig: Identity Provider configuration is invalid
IAM:
diff --git a/migrations/cockroach/V1.61__has_project.sql b/migrations/cockroach/V1.61__has_project.sql
new file mode 100644
index 0000000000..056d90933d
--- /dev/null
+++ b/migrations/cockroach/V1.61__has_project.sql
@@ -0,0 +1,13 @@
+ALTER TABLE management.projects ADD COLUMN has_project_check BOOLEAN;
+
+ALTER TABLE authz.applications ADD COLUMN has_project_check BOOLEAN;
+ALTER TABLE auth.applications ADD COLUMN has_project_check BOOLEAN;
+ALTER TABLE management.applications ADD COLUMN has_project_check BOOLEAN;
+
+CREATE TABLE auth.org_project_mapping (
+ org_id TEXT,
+ project_id TEXT,
+ project_grant_id TEXT,
+
+ PRIMARY KEY (org_id, project_id)
+);
\ No newline at end of file
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index c6758ffe3b..0ced5cc625 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -3452,6 +3452,7 @@ message AddProjectRequest {
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
bool project_role_assertion = 2;
bool project_role_check = 3;
+ bool has_project_check = 4;
}
message AddProjectResponse {
@@ -3464,6 +3465,7 @@ message UpdateProjectRequest {
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
bool project_role_assertion = 3;
bool project_role_check = 4;
+ bool has_project_check = 5;
}
message UpdateProjectResponse {
diff --git a/proto/zitadel/project.proto b/proto/zitadel/project.proto
index ecda3a2a62..01222a1c23 100644
--- a/proto/zitadel/project.proto
+++ b/proto/zitadel/project.proto
@@ -29,6 +29,8 @@ message Project {
bool project_role_assertion = 5;
// ZITADEL checks if the user has at least one on this project
bool project_role_check = 6;
+ // ZITADEL checks if the org of the user has permission to this project
+ bool has_project_check = 7;
}
message GrantedProject {