From baabc8676ee106c789b57d20bdf7b75891d846ee Mon Sep 17 00:00:00 2001 From: richardlt Date: Thu, 27 Feb 2025 11:11:57 +0000 Subject: [PATCH] feat(api): project mfa required for v2 routes Signed-off-by: richardlt --- engine/api/router_middleware_rbac.go | 6 +- engine/api/router_rbac_rule_admin.go | 5 +- engine/api/router_rbac_rule_cdn.go | 6 +- engine/api/router_rbac_rule_entity.go | 8 +-- engine/api/router_rbac_rule_entity_test.go | 6 +- engine/api/router_rbac_rule_global.go | 28 +++++---- engine/api/router_rbac_rule_hatchery.go | 9 ++- engine/api/router_rbac_rule_hooks.go | 6 +- engine/api/router_rbac_rule_plugin.go | 27 ++++----- engine/api/router_rbac_rule_project.go | 57 +++++++++++-------- engine/api/router_rbac_rule_project_test.go | 18 ++++-- engine/api/router_rbac_rule_queue.go | 14 ++--- engine/api/router_rbac_rule_region.go | 13 +++-- engine/api/router_rbac_rule_region_test.go | 6 +- engine/api/router_rbac_rule_user.go | 10 ++-- engine/api/router_rbac_rule_variableset.go | 25 ++++---- .../api/router_rbac_rule_variableset_test.go | 2 +- engine/api/router_rbac_rule_worker.go | 9 +-- engine/api/router_rbac_rule_workermodel.go | 9 +-- engine/api/router_rbac_rule_workflow.go | 41 +++++++------ engine/api/router_rbac_rule_workflow_test.go | 2 +- engine/api/v2_websocket.go | 9 ++- engine/service/http.go | 4 +- 23 files changed, 153 insertions(+), 167 deletions(-) diff --git a/engine/api/router_middleware_rbac.go b/engine/api/router_middleware_rbac.go index 517d8685bc..331b1e064e 100644 --- a/engine/api/router_middleware_rbac.go +++ b/engine/api/router_middleware_rbac.go @@ -4,11 +4,9 @@ import ( "context" "net/http" - "github.com/go-gorp/gorp" "github.com/gorilla/mux" "github.com/rockbears/log" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/engine/service" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" @@ -17,7 +15,7 @@ import ( func (api *API) rbacMiddleware(ctx context.Context, w http.ResponseWriter, req *http.Request, rc *service.HandlerConfig) (context.Context, error) { for _, checker := range rc.RbacCheckers { ctx := context.WithValue(ctx, cdslog.RbackCheckerName, sdk.GetFuncName(checker)) - if err := checker(ctx, getUserConsumer(ctx), api.Cache, api.mustDB(), mux.Vars(req)); err != nil { + if err := checker(ctx, mux.Vars(req)); err != nil { if isAdmin(ctx) { trackSudo(ctx, w) return ctx, nil @@ -29,7 +27,7 @@ func (api *API) rbacMiddleware(ctx context.Context, w http.ResponseWriter, req * return ctx, nil } -func (api *API) checkSessionPermission(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { +func (api *API) checkSessionPermission(ctx context.Context, _ map[string]string) error { if isCDN(ctx) { return nil } diff --git a/engine/api/router_rbac_rule_admin.go b/engine/api/router_rbac_rule_admin.go index 1de7c690fd..bf5b9ed7dc 100644 --- a/engine/api/router_rbac_rule_admin.go +++ b/engine/api/router_rbac_rule_admin.go @@ -3,13 +3,10 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) isAdmin(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) isAdmin(ctx context.Context, _ map[string]string) error { if isAdmin(ctx) { return nil } diff --git a/engine/api/router_rbac_rule_cdn.go b/engine/api/router_rbac_rule_cdn.go index 34f1c2c39b..3d2b08a29c 100644 --- a/engine/api/router_rbac_rule_cdn.go +++ b/engine/api/router_rbac_rule_cdn.go @@ -3,13 +3,11 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) isCDNService(_ context.Context, auth *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) isCDNService(ctx context.Context, _ map[string]string) error { + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } diff --git a/engine/api/router_rbac_rule_entity.go b/engine/api/router_rbac_rule_entity.go index ae700c4242..135fddd554 100644 --- a/engine/api/router_rbac_rule_entity.go +++ b/engine/api/router_rbac_rule_entity.go @@ -3,14 +3,10 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) entityRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] +func (api *API) entityRead(ctx context.Context, vars map[string]string) error { entityType := vars["entityType"] hatch := getHatcheryConsumer(ctx) @@ -22,5 +18,5 @@ func (api *API) entityRead(ctx context.Context, auth *sdk.AuthUserConsumer, stor if isHooks(ctx) { return nil } - return hasRoleOnProject(ctx, auth, store, db, projectKey, sdk.ProjectRoleRead) + return api.hasRoleOnProject(ctx, vars, sdk.ProjectRoleRead) } diff --git a/engine/api/router_rbac_rule_entity_test.go b/engine/api/router_rbac_rule_entity_test.go index 8cdc8905c1..51782517f4 100644 --- a/engine/api/router_rbac_rule_entity_test.go +++ b/engine/api/router_rbac_rule_entity_test.go @@ -42,7 +42,8 @@ projects: AuthentifiedUser: user1, }, } - require.NoError(t, api.entityRead(context.TODO(), &c, api.Cache, db, map[string]string{"projectKey": p.Key})) + require.NoError(t, api.entityRead(context.WithValue(context.TODO(), contextUserConsumer, &c), + map[string]string{"projectKey": p.Key})) cNo := sdk.AuthUserConsumer{ AuthConsumerUser: sdk.AuthUserConsumerData{ @@ -53,6 +54,7 @@ projects: }, }, } - err = api.entityRead(context.TODO(), &cNo, api.Cache, db, map[string]string{"projectKey": p.Key}) + err = api.entityRead(context.WithValue(context.TODO(), contextUserConsumer, &cNo), + map[string]string{"projectKey": p.Key}) require.True(t, sdk.ErrorIs(err, sdk.ErrForbidden)) } diff --git a/engine/api/router_rbac_rule_global.go b/engine/api/router_rbac_rule_global.go index da922ed96c..7177775cfd 100644 --- a/engine/api/router_rbac_rule_global.go +++ b/engine/api/router_rbac_rule_global.go @@ -3,20 +3,18 @@ package api import ( "context" - "github.com/go-gorp/gorp" - "github.com/ovh/cds/engine/api/rbac" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" ) -func hasGlobalRole(ctx context.Context, auth *sdk.AuthUserConsumer, _ cache.Store, db gorp.SqlExecutor, role string) error { +func (api *API) hasGlobalRole(ctx context.Context, role string) error { + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } - hasRole, err := rbac.HasGlobalRole(ctx, db, role, auth.AuthConsumerUser.AuthentifiedUser.ID) + hasRole, err := rbac.HasGlobalRole(ctx, api.mustDBWithCtx(ctx), role, auth.AuthConsumerUser.AuthentifiedUser.ID) if err != nil { return err } @@ -29,22 +27,22 @@ func hasGlobalRole(ctx context.Context, auth *sdk.AuthUserConsumer, _ cache.Stor } // GlobalPermissionManage return nil if the current AuthConsumer have the ProjectRoleManage on current project KEY -func (api *API) globalPermissionManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManagePermission) +func (api *API) globalPermissionManage(ctx context.Context, _ map[string]string) error { + return api.hasGlobalRole(ctx, sdk.GlobalRoleManagePermission) } -func (api *API) globalOrganizationManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManageOrganization) +func (api *API) globalOrganizationManage(ctx context.Context, _ map[string]string) error { + return api.hasGlobalRole(ctx, sdk.GlobalRoleManageOrganization) } -func (api *API) globalRegionManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManageRegion) +func (api *API) globalRegionManage(ctx context.Context, _ map[string]string) error { + return api.hasGlobalRole(ctx, sdk.GlobalRoleManageRegion) } -func (api *API) globalHatcheryManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManageHatchery) +func (api *API) globalHatcheryManage(ctx context.Context, _ map[string]string) error { + return api.hasGlobalRole(ctx, sdk.GlobalRoleManageHatchery) } -func (api *API) globalPluginManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, _ map[string]string) error { - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManagePlugin) +func (api *API) globalPluginManage(ctx context.Context, _ map[string]string) error { + return api.hasGlobalRole(ctx, sdk.GlobalRoleManagePlugin) } diff --git a/engine/api/router_rbac_rule_hatchery.go b/engine/api/router_rbac_rule_hatchery.go index f35c98f47c..7a981bf842 100644 --- a/engine/api/router_rbac_rule_hatchery.go +++ b/engine/api/router_rbac_rule_hatchery.go @@ -7,7 +7,6 @@ import ( "github.com/ovh/cds/engine/api/rbac" "github.com/ovh/cds/engine/api/region" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) @@ -32,16 +31,16 @@ func hatcheryHasRoleOnRegion(ctx context.Context, db gorp.SqlExecutor, hatcheryI return sdk.WithStack(sdk.ErrForbidden) } -func (api *API) isHatchery(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) isHatchery(ctx context.Context, _ map[string]string) error { if getHatcheryConsumer(ctx) != nil && getWorker(ctx) == nil { return nil } return sdk.WithStack(sdk.ErrForbidden) } -func (api *API) canRegenHatcheryToken(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - if err := api.isHatchery(ctx, auth, store, db, vars); err == nil { +func (api *API) canRegenHatcheryToken(ctx context.Context, vars map[string]string) error { + if err := api.isHatchery(ctx, vars); err == nil { return nil } - return hasGlobalRole(ctx, auth, store, db, sdk.GlobalRoleManageHatchery) + return api.hasGlobalRole(ctx, sdk.GlobalRoleManageHatchery) } diff --git a/engine/api/router_rbac_rule_hooks.go b/engine/api/router_rbac_rule_hooks.go index 234d080202..f8ee5db5ce 100644 --- a/engine/api/router_rbac_rule_hooks.go +++ b/engine/api/router_rbac_rule_hooks.go @@ -3,13 +3,11 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) isHookService(_ context.Context, auth *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) isHookService(ctx context.Context, _ map[string]string) error { + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } diff --git a/engine/api/router_rbac_rule_plugin.go b/engine/api/router_rbac_rule_plugin.go index 79a6a61755..ab83ffc756 100644 --- a/engine/api/router_rbac_rule_plugin.go +++ b/engine/api/router_rbac_rule_plugin.go @@ -1,23 +1,20 @@ package api import ( - "context" + "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" - "github.com/ovh/cds/sdk" + "github.com/ovh/cds/sdk" ) -func (api *API) pluginRead(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { - // Old worker - if isWorker(ctx) || getUserConsumer(ctx) != nil { - return nil - } - // New worker - if getWorker(ctx) != nil { - return nil - } +func (api *API) pluginRead(ctx context.Context, _ map[string]string) error { + // Old worker + if isWorker(ctx) || getUserConsumer(ctx) != nil { + return nil + } + // New worker + if getWorker(ctx) != nil { + return nil + } - return sdk.WithStack(sdk.ErrForbidden) + return sdk.WithStack(sdk.ErrForbidden) } diff --git a/engine/api/router_rbac_rule_project.go b/engine/api/router_rbac_rule_project.go index 9dc6d69deb..85b4292e0c 100644 --- a/engine/api/router_rbac_rule_project.go +++ b/engine/api/router_rbac_rule_project.go @@ -3,53 +3,60 @@ package api import ( "context" - "github.com/go-gorp/gorp" - + "github.com/ovh/cds/engine/api/database/gorpmapping" "github.com/ovh/cds/engine/api/rbac" - "github.com/ovh/cds/engine/cache" + "github.com/ovh/cds/engine/featureflipping" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" ) -func hasRoleOnProject(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, projectKey string, role string) error { - if auth == nil { +func (api *API) hasRoleOnProject(ctx context.Context, vars map[string]string, role string) error { + projectKey := vars["projectKey"] + + c := getUserConsumer(ctx) + if c == nil { return sdk.WithStack(sdk.ErrForbidden) } - hasRole, err := rbac.HasRoleOnProjectAndUserID(ctx, db, role, auth.AuthConsumerUser.AuthentifiedUser.ID, projectKey) + if supportMFA(ctx) && !isMFA(ctx) { + _, requireMFA := featureflipping.IsEnabled(ctx, gorpmapping.Mapper, api.mustDBWithCtx(ctx), sdk.FeatureMFARequired, map[string]string{ + "project_key": projectKey, + }) + if requireMFA { + return sdk.WithStack(sdk.ErrMFARequired) + } + } + + hasRole, err := rbac.HasRoleOnProjectAndUserID(ctx, api.mustDBWithCtx(ctx), role, c.AuthConsumerUser.AuthentifiedUser.ID, projectKey) if err != nil { return err } ctx = context.WithValue(ctx, cdslog.RbacRole, role) - if !hasRole { - return sdk.WithStack(sdk.ErrForbidden) + if hasRole { + return nil } - return nil + return sdk.WithStack(sdk.ErrForbidden) } // ProjectManage return nil if the current AuthUserConsumer have the ProjectRoleManage on current project KEY -func (api *API) projectManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] - return hasRoleOnProject(ctx, auth, store, db, projectKey, sdk.ProjectRoleManage) +func (api *API) projectManage(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnProject(ctx, vars, sdk.ProjectRoleManage) } // projectManageNotification return nil if the current AuthUserConsumer have the role ProjectRoleManageNotification on current project KEY -func (api *API) projectManageNotification(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] - return hasRoleOnProject(ctx, auth, store, db, projectKey, sdk.ProjectRoleManageNotification) +func (api *API) projectManageNotification(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnProject(ctx, vars, sdk.ProjectRoleManageNotification) } // projectManageVariableSet return nil if the current AuthUserConsumer have the role ProjectRoleManageVariableSet on current project KEY -func (api *API) projectManageVariableSet(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] - return hasRoleOnProject(ctx, auth, store, db, projectKey, sdk.ProjectRoleManageVariableSet) +func (api *API) projectManageVariableSet(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnProject(ctx, vars, sdk.ProjectRoleManageVariableSet) } // ProjectRead return nil if the current AuthUserConsumer have the ProjectRoleRead on current project KEY -func (api *API) projectRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] +func (api *API) projectRead(ctx context.Context, vars map[string]string) error { entityType := vars["entityType"] hatch := getHatcheryConsumer(ctx) @@ -58,19 +65,19 @@ func (api *API) projectRead(ctx context.Context, auth *sdk.AuthUserConsumer, sto return nil } - return hasRoleOnProject(ctx, auth, store, db, projectKey, sdk.ProjectRoleRead) + return api.hasRoleOnProject(ctx, vars, sdk.ProjectRoleRead) } -func (api *API) analysisRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) analysisRead(ctx context.Context, vars map[string]string) error { if isHooks(ctx) { return nil } - return api.projectRead(ctx, auth, store, db, vars) + return api.projectRead(ctx, vars) } -func (api *API) triggerAnalysis(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) triggerAnalysis(ctx context.Context, vars map[string]string) error { if isHooks(ctx) { return nil } - return api.projectManage(ctx, auth, store, db, vars) + return api.projectManage(ctx, vars) } diff --git a/engine/api/router_rbac_rule_project_test.go b/engine/api/router_rbac_rule_project_test.go index 6bfc5f0eae..fc0ca145a4 100644 --- a/engine/api/router_rbac_rule_project_test.go +++ b/engine/api/router_rbac_rule_project_test.go @@ -42,7 +42,8 @@ projects: AuthentifiedUser: user1, }, } - require.NoError(t, api.projectManageNotification(context.TODO(), &c, api.Cache, db, map[string]string{"projectKey": p.Key})) + require.NoError(t, api.projectManageNotification(context.WithValue(context.TODO(), contextUserConsumer, &c), + map[string]string{"projectKey": p.Key})) cNo := sdk.AuthUserConsumer{ AuthConsumerUser: sdk.AuthUserConsumerData{ @@ -53,7 +54,8 @@ projects: }, }, } - err = api.projectManageNotification(context.TODO(), &cNo, api.Cache, db, map[string]string{"projectKey": p.Key}) + err = api.projectManageNotification(context.WithValue(context.TODO(), contextUserConsumer, &cNo), + map[string]string{"projectKey": p.Key}) require.True(t, sdk.ErrorIs(err, sdk.ErrForbidden)) } @@ -86,7 +88,8 @@ projects: AuthentifiedUser: user1, }, } - require.NoError(t, api.projectManage(context.TODO(), &c, api.Cache, db, map[string]string{"projectKey": p.Key})) + require.NoError(t, api.projectManage(context.WithValue(context.TODO(), contextUserConsumer, &c), + map[string]string{"projectKey": p.Key})) cNo := sdk.AuthUserConsumer{ AuthConsumerUser: sdk.AuthUserConsumerData{ @@ -97,7 +100,8 @@ projects: }, }, } - err = api.projectManage(context.TODO(), &cNo, api.Cache, db, map[string]string{"projectKey": p.Key}) + err = api.projectManage(context.WithValue(context.TODO(), contextUserConsumer, &cNo), + map[string]string{"projectKey": p.Key}) require.True(t, sdk.ErrorIs(err, sdk.ErrForbidden)) } @@ -130,7 +134,8 @@ projects: AuthentifiedUser: user1, }, } - require.NoError(t, api.projectRead(context.TODO(), &c, api.Cache, db, map[string]string{"projectKey": p.Key})) + require.NoError(t, api.projectRead(context.WithValue(context.TODO(), contextUserConsumer, &c), + map[string]string{"projectKey": p.Key})) cNo := sdk.AuthUserConsumer{ AuthConsumerUser: sdk.AuthUserConsumerData{ @@ -141,6 +146,7 @@ projects: }, }, } - err = api.projectRead(context.TODO(), &cNo, api.Cache, db, map[string]string{"projectKey": p.Key}) + err = api.projectRead(context.WithValue(context.TODO(), contextUserConsumer, &cNo), + map[string]string{"projectKey": p.Key}) require.True(t, sdk.ErrorIs(err, sdk.ErrForbidden)) } diff --git a/engine/api/router_rbac_rule_queue.go b/engine/api/router_rbac_rule_queue.go index 4527601459..8e389099b3 100644 --- a/engine/api/router_rbac_rule_queue.go +++ b/engine/api/router_rbac_rule_queue.go @@ -3,31 +3,29 @@ package api import ( "context" - "github.com/go-gorp/gorp" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) // jobRunList only the hatchery can list job runs -func (api *API) jobRunListRegionalized(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) jobRunListRegionalized(ctx context.Context, vars map[string]string) error { hatchConsumer := getHatcheryConsumer(ctx) work := getWorker(ctx) if hatchConsumer == nil || work != nil { return sdk.WithStack(sdk.ErrForbidden) } - return hatcheryHasRoleOnRegion(ctx, db, hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) + return hatcheryHasRoleOnRegion(ctx, api.mustDBWithCtx(ctx), hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) } // jobRunRead only hatchery can read a job run for now -func (api *API) jobRunRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) jobRunRead(ctx context.Context, vars map[string]string) error { hatchConsumer := getHatcheryConsumer(ctx) work := getWorker(ctx) isCDN := isCDN(ctx) switch { // Hatchery case hatchConsumer != nil && work == nil: - return hatcheryHasRoleOnRegion(ctx, db, hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) + return hatcheryHasRoleOnRegion(ctx, api.mustDBWithCtx(ctx), hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) // Worker case hatchConsumer != nil && work != nil: if work.JobRunID == vars["runJobID"] { @@ -40,13 +38,13 @@ func (api *API) jobRunRead(ctx context.Context, auth *sdk.AuthUserConsumer, stor } // jobRunUpdate only hatchery and worker can update a job run -func (api *API) jobRunUpdate(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) jobRunUpdate(ctx context.Context, vars map[string]string) error { hatchConsumer := getHatcheryConsumer(ctx) work := getWorker(ctx) switch { // Hatchery case hatchConsumer != nil && work == nil: - return hatcheryHasRoleOnRegion(ctx, db, hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) + return hatcheryHasRoleOnRegion(ctx, api.mustDBWithCtx(ctx), hatchConsumer.AuthConsumerHatchery.HatcheryID, vars["regionName"], sdk.HatcheryRoleSpawn) // Worker case hatchConsumer != nil && work != nil: if work.JobRunID == vars["runJobID"] { diff --git a/engine/api/router_rbac_rule_region.go b/engine/api/router_rbac_rule_region.go index 27111aab12..0bb43a5be1 100644 --- a/engine/api/router_rbac_rule_region.go +++ b/engine/api/router_rbac_rule_region.go @@ -8,18 +8,20 @@ import ( "github.com/ovh/cds/engine/api/organization" "github.com/ovh/cds/engine/api/rbac" "github.com/ovh/cds/engine/api/region" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" "github.com/ovh/cds/sdk/telemetry" ) -func hasRoleOnRegion(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, regionIdentifier string, role string) error { +func (api *API) hasRoleOnRegion(ctx context.Context, vars map[string]string, role string) error { + regionIdentifier := vars["regionIdentifier"] + + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } - hasRole, err := hasRoleOnRegionAndUserID(ctx, db, role, auth.AuthConsumerUser.AuthentifiedUser, regionIdentifier) + hasRole, err := hasRoleOnRegionAndUserID(ctx, api.mustDBWithCtx(ctx), role, auth.AuthConsumerUser.AuthentifiedUser, regionIdentifier) if err != nil { return err } @@ -75,7 +77,6 @@ func hasRoleOnRegionAndUserID(ctx context.Context, db gorp.SqlExecutor, role str } // RegionRead return nil if the current AuthConsumer have the ProjectRoleRead on current project KEY -func (api *API) regionRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - regionIdentifier := vars["regionIdentifier"] - return hasRoleOnRegion(ctx, auth, store, db, regionIdentifier, sdk.RegionRoleList) +func (api *API) regionRead(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnRegion(ctx, vars, sdk.RegionRoleList) } diff --git a/engine/api/router_rbac_rule_region_test.go b/engine/api/router_rbac_rule_region_test.go index 4daec19f7a..4233bf64b4 100644 --- a/engine/api/router_rbac_rule_region_test.go +++ b/engine/api/router_rbac_rule_region_test.go @@ -3,9 +3,10 @@ package api import ( "context" "fmt" - "github.com/rockbears/yaml" "testing" + "github.com/rockbears/yaml" + "github.com/ovh/cds/engine/api/rbac" "github.com/ovh/cds/engine/api/region" "github.com/ovh/cds/engine/api/test/assets" @@ -47,5 +48,6 @@ regions: AuthentifiedUser: user1, }, } - require.NoError(t, api.regionRead(context.TODO(), &c, api.Cache, db, map[string]string{"regionIdentifier": reg.Name})) + require.NoError(t, api.regionRead(context.WithValue(context.TODO(), contextUserConsumer, &c), + map[string]string{"regionIdentifier": reg.Name})) } diff --git a/engine/api/router_rbac_rule_user.go b/engine/api/router_rbac_rule_user.go index 24c5ea5745..c0ab67a20c 100644 --- a/engine/api/router_rbac_rule_user.go +++ b/engine/api/router_rbac_rule_user.go @@ -3,17 +3,15 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) isCurrentUser(_ context.Context, auth *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, vars map[string]string) error { - if auth == nil { +func (api *API) isCurrentUser(ctx context.Context, vars map[string]string) error { + c := getUserConsumer(ctx) + if c == nil { return sdk.WithStack(sdk.ErrForbidden) } - if vars["user"] == auth.AuthConsumerUser.AuthentifiedUser.Username { + if vars["user"] == c.AuthConsumerUser.AuthentifiedUser.Username { return nil } return sdk.WithStack(sdk.ErrForbidden) diff --git a/engine/api/router_rbac_rule_variableset.go b/engine/api/router_rbac_rule_variableset.go index 6cd01986e7..8ef867cae9 100644 --- a/engine/api/router_rbac_rule_variableset.go +++ b/engine/api/router_rbac_rule_variableset.go @@ -3,20 +3,21 @@ package api import ( "context" - "github.com/go-gorp/gorp" - "github.com/ovh/cds/engine/api/rbac" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" ) -func hasRoleOnVariableSet(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, projectKey string, variableset string, role string) error { +func (api *API) hasRoleOnVariableSet(ctx context.Context, vars map[string]string, role string) error { + projectKey := vars["projectKey"] + variableSetName := vars["variableSetName"] + + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } - hasRole, err := rbac.HasRoleOnVariableSetAndUserID(ctx, db, role, auth.AuthConsumerUser.AuthentifiedUser.ID, projectKey, variableset) + hasRole, err := rbac.HasRoleOnVariableSetAndUserID(ctx, api.mustDBWithCtx(ctx), role, auth.AuthConsumerUser.AuthentifiedUser.ID, projectKey, variableSetName) if err != nil { return err } @@ -30,16 +31,10 @@ func hasRoleOnVariableSet(ctx context.Context, auth *sdk.AuthUserConsumer, store } // workflowTrigger return nil if the current AuthUserConsumer have the WorkflowRoleTrigger on current workflow -func (api *API) variableSetItemManage(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] - vsName := vars["variableSetName"] - - return hasRoleOnVariableSet(ctx, auth, store, db, projectKey, vsName, sdk.VariableSetRoleManageItem) +func (api *API) variableSetItemManage(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnVariableSet(ctx, vars, sdk.VariableSetRoleManageItem) } -func (api *API) variableSetItemRead(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { - projectKey := vars["projectKey"] - vsName := vars["variableSetName"] - - return hasRoleOnVariableSet(ctx, auth, store, db, projectKey, vsName, sdk.VariableSetRoleUse) +func (api *API) variableSetItemRead(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnVariableSet(ctx, vars, sdk.VariableSetRoleUse) } diff --git a/engine/api/router_rbac_rule_variableset_test.go b/engine/api/router_rbac_rule_variableset_test.go index 6f30bf3bde..3f635645ba 100644 --- a/engine/api/router_rbac_rule_variableset_test.go +++ b/engine/api/router_rbac_rule_variableset_test.go @@ -105,7 +105,7 @@ variablesets: require.NoError(t, rbacLoader.FillRBACWithIDs(context.TODO(), &r)) require.NoError(t, rbac.Insert(context.TODO(), db, &r)) - err = api.variableSetItemManage(context.TODO(), &auth, api.Cache, api.mustDB(), map[string]string{ + err = api.variableSetItemManage(context.WithValue(context.TODO(), contextUserConsumer, &auth), map[string]string{ "projectKey": proj.Key, "variableSetName": targetVariableSet, }) diff --git a/engine/api/router_rbac_rule_worker.go b/engine/api/router_rbac_rule_worker.go index 1d19890902..cd39d87c4d 100644 --- a/engine/api/router_rbac_rule_worker.go +++ b/engine/api/router_rbac_rule_worker.go @@ -3,13 +3,10 @@ package api import ( "context" - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" ) -func (api *API) isWorker(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) isWorker(ctx context.Context, _ map[string]string) error { hc := getHatcheryConsumer(ctx) work := getWorker(ctx) if hc != nil && work != nil { @@ -18,14 +15,14 @@ func (api *API) isWorker(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.S return sdk.WithStack(sdk.ErrForbidden) } -func (api *API) workerGet(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) workerGet(ctx context.Context, _ map[string]string) error { if isCDN(ctx) { return nil } return sdk.WithStack(sdk.ErrForbidden) } -func (api *API) workerList(ctx context.Context, _ *sdk.AuthUserConsumer, _ cache.Store, _ gorp.SqlExecutor, _ map[string]string) error { +func (api *API) workerList(ctx context.Context, _ map[string]string) error { hc := getHatcheryConsumer(ctx) if isAdmin(ctx) || hc != nil { return nil diff --git a/engine/api/router_rbac_rule_workermodel.go b/engine/api/router_rbac_rule_workermodel.go index 09587e2e32..adcc5662fd 100644 --- a/engine/api/router_rbac_rule_workermodel.go +++ b/engine/api/router_rbac_rule_workermodel.go @@ -2,16 +2,11 @@ package api import ( "context" - - "github.com/go-gorp/gorp" - - "github.com/ovh/cds/engine/cache" - "github.com/ovh/cds/sdk" ) -func (api *API) workerModelRead(ctx context.Context, c *sdk.AuthUserConsumer, cache cache.Store, db gorp.SqlExecutor, vars map[string]string) error { +func (api *API) workerModelRead(ctx context.Context, vars map[string]string) error { if getHatcheryConsumer(ctx) != nil { return nil } - return api.projectRead(ctx, c, cache, db, vars) + return api.projectRead(ctx, vars) } diff --git a/engine/api/router_rbac_rule_workflow.go b/engine/api/router_rbac_rule_workflow.go index 7698b74d0a..04c081db66 100644 --- a/engine/api/router_rbac_rule_workflow.go +++ b/engine/api/router_rbac_rule_workflow.go @@ -4,42 +4,25 @@ import ( "context" "net/url" - "github.com/go-gorp/gorp" - "github.com/ovh/cds/engine/api/rbac" "github.com/ovh/cds/engine/api/workflow_v2" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" cdslog "github.com/ovh/cds/sdk/log" ) -func hasRoleOnWorkflow(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, projectKey string, vcs, repo, workflowName string, role string) error { +func (api *API) hasRoleOnWorkflow(ctx context.Context, vars map[string]string, role string) error { + auth := getUserConsumer(ctx) if auth == nil { return sdk.WithStack(sdk.ErrForbidden) } - hasRole, err := rbac.HasRoleOnWorkflowAndUserID(ctx, db, role, auth.AuthConsumerUser.AuthentifiedUser.ID, projectKey, vcs, repo, workflowName) - if err != nil { - return err - } - - ctx = context.WithValue(ctx, cdslog.RbacRole, role) - if !hasRole { - return sdk.WithStack(sdk.ErrForbidden) - } - - return nil -} - -// workflowTrigger return nil if the current AuthUserConsumer have the WorkflowRoleTrigger on current workflow -func (api *API) workflowTrigger(ctx context.Context, auth *sdk.AuthUserConsumer, store cache.Store, db gorp.SqlExecutor, vars map[string]string) error { projectKey := vars["projectKey"] workflowName := vars["workflowName"] workflowRunID := vars["workflowRunID"] var vcsName, repoName string if workflowRunID != "" { - run, err := workflow_v2.LoadRunByID(ctx, db, workflowRunID) + run, err := workflow_v2.LoadRunByID(ctx, api.mustDBWithCtx(ctx), workflowRunID) if err != nil { return err } @@ -72,5 +55,21 @@ func (api *API) workflowTrigger(ctx context.Context, auth *sdk.AuthUserConsumer, repoName = repositoryIdentifier } } - return hasRoleOnWorkflow(ctx, auth, store, db, projectKey, vcsName, repoName, workflowName, sdk.WorkflowRoleTrigger) + + hasRole, err := rbac.HasRoleOnWorkflowAndUserID(ctx, api.mustDBWithCtx(ctx), role, auth.AuthConsumerUser.AuthentifiedUser.ID, projectKey, vcsName, repoName, workflowName) + if err != nil { + return err + } + + ctx = context.WithValue(ctx, cdslog.RbacRole, role) + if !hasRole { + return sdk.WithStack(sdk.ErrForbidden) + } + + return nil +} + +// workflowTrigger return nil if the current AuthUserConsumer have the WorkflowRoleTrigger on current workflow +func (api *API) workflowTrigger(ctx context.Context, vars map[string]string) error { + return api.hasRoleOnWorkflow(ctx, vars, sdk.WorkflowRoleTrigger) } diff --git a/engine/api/router_rbac_rule_workflow_test.go b/engine/api/router_rbac_rule_workflow_test.go index 8425503de5..a329620eb1 100644 --- a/engine/api/router_rbac_rule_workflow_test.go +++ b/engine/api/router_rbac_rule_workflow_test.go @@ -117,7 +117,7 @@ workflows: require.NoError(t, rbacLoader.FillRBACWithIDs(context.TODO(), &r)) require.NoError(t, rbac.Insert(context.TODO(), db, &r)) - err = api.workflowTrigger(context.TODO(), &auth, api.Cache, api.mustDB(), map[string]string{ + err = api.workflowTrigger(context.WithValue(context.TODO(), contextUserConsumer, &auth), map[string]string{ "projectKey": proj.Key, "vcsIdentifier": vcs.Name, "repositoryIdentifier": repo.Name, diff --git a/engine/api/v2_websocket.go b/engine/api/v2_websocket.go index 904b970a37..9d75e659ca 100644 --- a/engine/api/v2_websocket.go +++ b/engine/api/v2_websocket.go @@ -11,6 +11,7 @@ import ( "github.com/go-gorp/gorp" "github.com/rockbears/log" + "github.com/ovh/cds/engine/api/rbac" "github.com/ovh/cds/engine/api/workflow_v2" "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/engine/service" @@ -112,7 +113,13 @@ func (c *websocketV2ClientData) updateEventFilters(ctx context.Context, db gorp. if isMaintainer { continue } - return hasRoleOnProject(ctx, &c.AuthConsumer, cache, db, f.ProjectKey, sdk.ProjectRoleRead) + hasRole, err := rbac.HasRoleOnProjectAndUserID(ctx, db, sdk.ProjectRoleRead, c.AuthConsumer.AuthConsumerUser.AuthentifiedUser.ID, f.ProjectKey) + if err != nil { + return err + } + if !hasRole { + return sdk.WithStack(sdk.ErrForbidden) + } } } diff --git a/engine/service/http.go b/engine/service/http.go index 914d33d98e..894e94e7a0 100644 --- a/engine/service/http.go +++ b/engine/service/http.go @@ -13,12 +13,10 @@ import ( "sync" "time" - "github.com/go-gorp/gorp" "github.com/rockbears/log" "github.com/rockbears/yaml" "gopkg.in/spacemonkeygo/httpsig.v0" - "github.com/ovh/cds/engine/cache" "github.com/ovh/cds/sdk" "github.com/ovh/cds/sdk/cdsclient" cdslog "github.com/ovh/cds/sdk/log" @@ -26,7 +24,7 @@ import ( // Handler defines the HTTP handler used in CDS engine type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request) error -type RbacChecker func(ctx context.Context, auth *sdk.AuthUserConsumer, cache cache.Store, db gorp.SqlExecutor, vars map[string]string) error +type RbacChecker func(ctx context.Context, vars map[string]string) error type RbacCheckers []RbacChecker func RBAC(checkers ...RbacChecker) []RbacChecker {