mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:57:33 +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:
@@ -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,
|
||||
|
Reference in New Issue
Block a user