Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add rbac middleware system #6103

Merged
merged 13 commits into from
Mar 21, 2022
4 changes: 3 additions & 1 deletion engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (api *API) InitRouter() {
api.Router.SetHeaderFunc = service.DefaultHeaders
api.Router.Middlewares = append(api.Router.Middlewares, api.tracingMiddleware, api.jwtMiddleware)
api.Router.DefaultAuthMiddleware = api.authMiddleware
api.Router.PostAuthMiddlewares = append(api.Router.PostAuthMiddlewares, api.xsrfMiddleware, api.maintenanceMiddleware)
api.Router.PostAuthMiddlewares = append(api.Router.PostAuthMiddlewares, api.xsrfMiddleware, api.maintenanceMiddleware, api.rbacMiddleware)
api.Router.PostMiddlewares = append(api.Router.PostMiddlewares, service.TracingPostMiddleware)

r := api.Router
Expand Down Expand Up @@ -449,6 +449,8 @@ func (api *API) InitRouter() {
r.Handle("/template/{groupName}/{templateSlug}/instance/{instanceID}", Scope(sdk.AuthConsumerScopeTemplate), r.DELETE(api.deleteTemplateInstanceHandler))
r.Handle("/template/{groupName}/{templateSlug}/usage", Scope(sdk.AuthConsumerScopeTemplate), r.GET(api.getTemplateUsageHandler))

r.Handle("/v2/project/{projectKey}/vcs", nil, r.POSTv2(api.postVCSOnProjectHandler))

//Not Found handler
r.Mux.NotFoundHandler = http.HandlerFunc(r.NotFoundHandler)

Expand Down
30 changes: 30 additions & 0 deletions engine/api/group/dao_rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package group

import (
"context"

"github.com/go-gorp/gorp"

"github.com/ovh/cds/engine/api/database/gorpmapping"
"github.com/ovh/cds/sdk"
)

func LoadGroupByRbacGlobal(ctx context.Context, db gorp.SqlExecutor, rbacGlobalID int64) ([]sdk.Group, error) {
sguiheux marked this conversation as resolved.
Show resolved Hide resolved
query := `
SELECT g.*
FROM rbac_global_groups rgg
JOIN "group" g ON g.id = rgg.group_id
WHERE rgg.rbac_global_id = $1
`
return getAll(ctx, db, gorpmapping.NewQuery(query).Args(rbacGlobalID))
}

func LoadGroupByRbacProject(ctx context.Context, db gorp.SqlExecutor, rbacProjectID int64) ([]sdk.Group, error) {
sguiheux marked this conversation as resolved.
Show resolved Hide resolved
query := `
SELECT g.*
FROM rbac_project_groups rpg
JOIN "group" g ON g.id = rpg.group_id
WHERE rpg.rbac_project_id = $1
`
return getAll(ctx, db, gorpmapping.NewQuery(query).Args(rbacProjectID))
}
72 changes: 72 additions & 0 deletions engine/api/project/dao_rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package project

import (
"context"
"database/sql"

"github.com/go-gorp/gorp"

"github.com/ovh/cds/sdk"
)

func LoadRbacProjectIDsByUserID(_ context.Context, db gorp.SqlExecutor, role string, userID string) ([]sdk.Project, error) {
query := `
WITH userRbac as (
SELECT distinct(rpi.project_id) as id
FROM rbac_project_ids rpi
JOIN rbac_project rp ON rp.id = rpi.rbac_project_id AND rp.role = $1
JOIN rbac_project_users rpu ON rpu.rbac_project_id = rp.id AND rpu.user_id = $2
),
groupRbac as (
SELECT distinct(rpi.project_id) as id
FROM rbac_project_ids rpi
JOIN rbac_project rp ON rp.id = rpi.rbac_project_id AND rp.role = $1
JOIN rbac_project_groups rpg ON rpg.rbac_project_id = rp.id
JOIN "group" g ON g.id = rpg.group_id
JOIN group_authentified_user gau ON gau.group_id = g.id AND gau.authentified_user_id = $2
),
userAllRbac as (
SELECT distinct(p.id) as id
FROM project p
JOIN rbac_project_ids rpi ON rpi.project_id = p.id
JOIN rbac_project rp ON rp.id = rpi.rbac_project_id AND rp.all = true AND rp.role = $1
JOIN rbac_project_users rpu ON rpu.rbac_project_id = rp.id AND rpu.user_id = $2
),
groupAllRbac as (
SELECT distinct(p.id) as id
FROM project p
JOIN rbac_project_ids rpi ON rpi.project_id = p.id
JOIN rbac_project rp ON rp.id = rpi.rbac_project_id AND rp.role = $1 AND rp.all = true
JOIN rbac_project_groups rpg ON rpg.rbac_project_id = rp.id
JOIN "group" g ON g.id = rpg.group_id
JOIN group_authentified_user gau ON gau.group_id = g.id AND gau.authentified_user_id = $2
),
concat as (
SELECT distinct(id) as id FROM (
SELECT id FROM userRbac UNION SELECT id FROM groupRbac UNION SELECT id FROM userAllRbac UNION SELECT id FROM groupAllRbac
) tmp
)
SELECT p.* FROM concat c
JOIN project p ON p.id = c.id`
var projects []sdk.Project
if _, err := db.Select(&projects, query, role, userID); err != nil {
return nil, err
}
return projects, nil
}

func LoadProjectByRbacProject(ctx context.Context, db gorp.SqlExecutor, rbacProjectID int64) ([]sdk.ProjectIdentifiers, error) {
query := `
SELECT p.id, p.projectkey
FROM rbac_project_ids rpi
JOIN project p ON p.id = rpi.project_id
WHERE rpi.rbac_project_id = $1`
var res []sdk.ProjectIdentifiers
if _, err := db.Select(&res, query, rbacProjectID); err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, sdk.WithStack(err)
}
return res, nil
}
99 changes: 99 additions & 0 deletions engine/api/rbac/dao_rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package rbac

import (
"context"
"time"

"github.com/go-gorp/gorp"
"github.com/rockbears/log"

"github.com/ovh/cds/engine/api/database/gorpmapping"
"github.com/ovh/cds/engine/gorpmapper"
"github.com/ovh/cds/sdk"
)

func LoadRbacByName(ctx context.Context, db gorp.SqlExecutor, name string, opts ...LoadOptionFunc) (sdk.RBAC, error) {
query := `SELECT * FROM rbac WHERE name = $1`
return get(ctx, db, gorpmapping.NewQuery(query).Args(name), opts...)
}

// Insert a RBAC permission in database
func Insert(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rb *sdk.RBAC) error {
if err := sdk.IsValidRbac(rb); err != nil {
return err
}
if rb.UUID == "" {
rb.UUID = sdk.UUID()
}
if rb.Created.IsZero() {
rb.Created = time.Now()
}
rb.LastModified = time.Now()
dbRb := rbac{RBAC: *rb}
if err := gorpmapping.InsertAndSign(ctx, db, &dbRb); err != nil {
return err
}

for i := range rb.Globals {
dbRbGlobal := rbacGlobal{
RbacUUID: dbRb.UUID,
RBACGlobal: rb.Globals[i],
}
if err := insertRbacGlobal(ctx, db, &dbRbGlobal); err != nil {
return err
}
}
for i := range rb.Projects {
dbRbProject := rbacProject{
RbacUUID: dbRb.UUID,
RBACProject: rb.Projects[i],
}
if err := insertRbacProject(ctx, db, &dbRbProject); err != nil {
return err
}
}
*rb = dbRb.RBAC
return nil
}

func Update(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rb *sdk.RBAC) error {
if err := Delete(ctx, db, *rb); err != nil {
sguiheux marked this conversation as resolved.
Show resolved Hide resolved
return err
}
return Insert(ctx, db, rb)
sguiheux marked this conversation as resolved.
Show resolved Hide resolved
}

func Delete(_ context.Context, db gorpmapper.SqlExecutorWithTx, rb sdk.RBAC) error {
dbRb := rbac{RBAC: rb}
if err := gorpmapping.Delete(db, &dbRb); err != nil {
return err
}
return nil
}

func get(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query, opts ...LoadOptionFunc) (sdk.RBAC, error) {
var r sdk.RBAC
var rbacDB rbac
found, err := gorpmapping.Get(ctx, db, q, &rbacDB)
if err != nil {
return r, err
}
if !found {
return r, sdk.WithStack(sdk.ErrNotFound)
}

isValid, err := gorpmapping.CheckSignature(rbacDB, rbacDB.Signature)
if err != nil {
return r, sdk.WrapError(err, "error when checking signature for rbac %s", rbacDB.UUID)
}
if !isValid {
log.Error(ctx, "rbac.get> rbac %s (%s) data corrupted", rbacDB.Name, rbacDB.UUID)
}
for _, f := range opts {
if err := f(ctx, db, &rbacDB); err != nil {
return r, err
}
}
r = rbacDB.RBAC
return r, nil
}
80 changes: 80 additions & 0 deletions engine/api/rbac/dao_rbac_global.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package rbac

import (
"context"

"github.com/go-gorp/gorp"

"github.com/ovh/cds/engine/api/database/gorpmapping"
"github.com/ovh/cds/engine/api/group"
"github.com/ovh/cds/engine/api/user"
"github.com/ovh/cds/engine/gorpmapper"
)

func insertRbacGlobal(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rg *rbacGlobal) error {
if err := gorpmapping.InsertAndSign(ctx, db, rg); err != nil {
return err
}

for _, userID := range rg.RBACUsersIDs {
if err := insertRbacGlobalUser(ctx, db, rg.ID, userID); err != nil {
return err
}
}
for _, groupID := range rg.RBACGroupsIDs {
if err := insertRbacGlobalGroup(ctx, db, rg.ID, groupID); err != nil {
return err
}
}
return nil
}

func insertRbacGlobalUser(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacGlobalID int64, userID string) error {
rgu := rbacGlobalUser{
RbacGlobalID: rbacGlobalID,
RbacGlobalUserID: userID,
}
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
return err
}
return nil
}

func insertRbacGlobalGroup(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacGlobalID int64, groupID int64) error {
rgu := rbacGlobalGroup{
RbacGlobalID: rbacGlobalID,
RbacGlobalGroupID: groupID,
}
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
return err
}
return nil
}

func loadRbacGlobalUsersTargeted(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
users, err := user.LoadUsersByRbacGlobal(ctx, db, rbacGlobal.ID)
if err != nil {
return err
}
rbacGlobal.RBACUsersName = make([]string, 0, len(users))
rbacGlobal.RBACUsersIDs = make([]string, 0, len(users))
for _, u := range users {
rbacGlobal.RBACUsersName = append(rbacGlobal.RBACUsersName, u.Username)
rbacGlobal.RBACUsersIDs = append(rbacGlobal.RBACUsersIDs, u.ID)
}
return nil
}

func loadRbacGlobalGroupsTargeted(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
groups, err := group.LoadGroupByRbacGlobal(ctx, db, rbacGlobal.ID)
sguiheux marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
rbacGlobal.RBACGroupsName = make([]string, 0, len(groups))
rbacGlobal.RBACGroupsIDs = make([]int64, 0, len(groups))
for _, g := range groups {
rbacGlobal.RBACGroupsName = append(rbacGlobal.RBACGroupsName, g.Name)
rbacGlobal.RBACGroupsIDs = append(rbacGlobal.RBACGroupsIDs, g.ID)
}
return nil
}
Loading