mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-01 14:30:49 +00:00
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:
parent
0b3155b8ab
commit
0ab973b967
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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 | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -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 | |
|
||||
|
||||
|
||||
|
||||
|
@ -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 /> |
|
||||
|
||||
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -10,6 +10,7 @@ func projectWriteModelToProject(writeModel *ProjectWriteModel) *domain.Project {
|
||||
Name: writeModel.Name,
|
||||
ProjectRoleAssertion: writeModel.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: writeModel.ProjectRoleCheck,
|
||||
HasProjectCheck: writeModel.HasProjectCheck,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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))
|
||||
|
@ -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(
|
||||
|
@ -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 {
|
||||
|
@ -11,6 +11,7 @@ type Project struct {
|
||||
Name string
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
HasProjectCheck bool
|
||||
}
|
||||
|
||||
type ProjectState int32
|
||||
|
@ -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)
|
||||
|
53
internal/project/model/org_project_mapping_view.go
Normal file
53
internal/project/model/org_project_mapping_view.go
Normal 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
|
||||
}
|
@ -17,6 +17,7 @@ type Project struct {
|
||||
Grants []*ProjectGrant
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
HasProjectCheck bool
|
||||
}
|
||||
type ProjectChanges struct {
|
||||
Changes []*ProjectChange
|
||||
|
@ -16,6 +16,7 @@ type ProjectView struct {
|
||||
ResourceOwner string
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
HasProjectCheck bool
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;"`
|
||||
}
|
@ -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 ""
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
51
internal/project/repository/view/org_project_mapping_view.go
Normal file
51
internal/project/repository/view/org_project_mapping_view.go
Normal 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)
|
||||
}
|
@ -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),
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
13
migrations/cockroach/V1.61__has_project.sql
Normal file
13
migrations/cockroach/V1.61__has_project.sql
Normal 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)
|
||||
);
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user