feat: check has project (#2206)

* feat: define org grant check on project

* feat: has project check

* feat: has project check

* feat: check has project

* feat: check has project

* feat: add has project check to console

* Update internal/auth/repository/eventsourcing/eventstore/auth_request.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request_test.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request_test.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/auth/repository/eventsourcing/eventstore/auth_request_test.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/ui/login/static/i18n/en.yaml

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* fix: add has project tests

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2021-08-18 10:49:04 +02:00 committed by GitHub
parent 0b3155b8ab
commit 0ab973b967
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 732 additions and 190 deletions

View File

@ -89,6 +89,9 @@
<mat-checkbox [(ngModel)]="project.projectRoleCheck" (change)="saveProject()" color="primary">
{{'PROJECT.ROLE.CHECK' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p>
<mat-checkbox [(ngModel)]="project.hasProjectCheck" (change)="saveProject()" color="primary">
{{'PROJECT.HAS_PROJECT' | translate}}</mat-checkbox>
<p class="desc">{{'PROJECT.HAS_PROJECT_DESCRIPTION' | translate}}</p>
<div class="divider"></div>
<app-project-roles
[disabled]="(['project.role.write$', 'project.role.write:'+ project.id]| hasRole | async) == false"

View File

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

View File

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

View File

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

View File

@ -3276,6 +3276,7 @@ This is an empty request
| name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| 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<br /> string.max_len: 200<br /> |
| project_role_assertion | bool | - | |
| project_role_check | bool | - | |
| has_project_check | bool | - | |

View File

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

View File

@ -39,6 +39,32 @@ title: zitadel/text.proto
### ExternalRegistrationUserOverviewScreenText
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| title | string | - | string.max_len: 200<br /> |
| description | string | - | string.max_len: 500<br /> |
| email_label | string | - | string.max_len: 200<br /> |
| username_label | string | - | string.max_len: 200<br /> |
| firstname_label | string | - | string.max_len: 200<br /> |
| lastname_label | string | - | string.max_len: 200<br /> |
| nickname_label | string | - | string.max_len: 200<br /> |
| language_label | string | - | string.max_len: 200<br /> |
| phone_label | string | - | string.max_len: 200<br /> |
| tos_and_privacy_label | string | - | string.max_len: 200<br /> |
| tos_confirm | string | - | string.max_len: 200<br /> |
| tos_link_text | string | - | string.max_len: 200<br /> |
| tos_confirm_and | string | - | string.max_len: 200<br /> |
| privacy_link_text | string | - | string.max_len: 200<br /> |
| back_button_text | string | - | string.max_len: 200<br /> |
| next_button_text | string | - | string.max_len: 200<br /> |
### ExternalUserNotFoundScreenText
@ -49,6 +75,11 @@ title: zitadel/text.proto
| description | string | - | string.max_len: 500<br /> |
| link_button_text | string | - | string.max_len: 100<br /> |
| auto_register_button_text | string | - | string.max_len: 100<br /> |
| tos_and_privacy_label | string | - | string.max_len: 200<br /> |
| tos_confirm | string | - | string.max_len: 200<br /> |
| tos_link_text | string | - | string.max_len: 200<br /> |
| privacy_link_text | string | - | string.max_len: 200<br /> |
| tos_confirm_and | string | - | string.max_len: 200<br /> |
@ -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<br /> |
| description | string | - | string.max_len: 500<br /> |
| next_button_text | string | - | string.max_len: 100<br /> |
| cancel_button_text | string | - | string.max_len: 100<br /> |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ func projectWriteModelToProject(writeModel *ProjectWriteModel) *domain.Project {
Name: writeModel.Name,
ProjectRoleAssertion: writeModel.ProjectRoleAssertion,
ProjectRoleCheck: writeModel.ProjectRoleCheck,
HasProjectCheck: writeModel.HasProjectCheck,
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,6 +11,7 @@ type Project struct {
Name string
ProjectRoleAssertion bool
ProjectRoleCheck bool
HasProjectCheck bool
}
type ProjectState int32

View File

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

View File

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

View File

@ -17,6 +17,7 @@ type Project struct {
Grants []*ProjectGrant
ProjectRoleAssertion bool
ProjectRoleCheck bool
HasProjectCheck bool
}
type ProjectChanges struct {
Changes []*ProjectChange

View File

@ -16,6 +16,7 @@ type ProjectView struct {
ResourceOwner string
ProjectRoleAssertion bool
ProjectRoleCheck bool
HasProjectCheck bool
Sequence uint64
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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