Skip to content

Commit

Permalink
feat(api): project mfa required for v2 routes
Browse files Browse the repository at this point in the history
Signed-off-by: richardlt <[email protected]>
  • Loading branch information
richardlt committed Feb 27, 2025
1 parent 13f74ff commit baabc86
Show file tree
Hide file tree
Showing 23 changed files with 153 additions and 167 deletions.
6 changes: 2 additions & 4 deletions engine/api/router_middleware_rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
}
Expand Down
5 changes: 1 addition & 4 deletions engine/api/router_rbac_rule_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
6 changes: 2 additions & 4 deletions engine/api/router_rbac_rule_cdn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 2 additions & 6 deletions engine/api/router_rbac_rule_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
}
6 changes: 4 additions & 2 deletions engine/api/router_rbac_rule_entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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))
}
28 changes: 13 additions & 15 deletions engine/api/router_rbac_rule_global.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
}
9 changes: 4 additions & 5 deletions engine/api/router_rbac_rule_hatchery.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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)
}
6 changes: 2 additions & 4 deletions engine/api/router_rbac_rule_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
27 changes: 12 additions & 15 deletions engine/api/router_rbac_rule_plugin.go
Original file line number Diff line number Diff line change
@@ -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)
}
57 changes: 32 additions & 25 deletions engine/api/router_rbac_rule_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
}
Loading

0 comments on commit baabc86

Please sign in to comment.