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
45 changed files with 732 additions and 190 deletions

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,