Skip to content

Commit

Permalink
feat: add rbac middleware system (#6103)
Browse files Browse the repository at this point in the history
  • Loading branch information
sguiheux authored Mar 21, 2022
1 parent 1698e23 commit fce41f8
Show file tree
Hide file tree
Showing 29 changed files with 1,556 additions and 20 deletions.
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
100 changes: 100 additions & 0 deletions engine/api/rbac/dao_rbac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 {
return err
}
return Insert(ctx, db, rb)
}

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)
return r, sdk.WithStack(sdk.ErrNotFound)
}
for _, f := range opts {
if err := f(ctx, db, &rbacDB); err != nil {
return r, err
}
}
r = rbacDB.RBAC
return r, nil
}
94 changes: 94 additions & 0 deletions engine/api/rbac/dao_rbac_global.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package rbac

import (
"context"

"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 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 getAllRBACGlobalUsers(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
q := gorpmapping.NewQuery("SELECT * FROM rbac_global_users WHERE rbac_global_id = $1").Args(rbacGlobal.ID)
var rbacUserIDS []rbacGlobalUser
if err := gorpmapping.GetAll(ctx, db, q, &rbacUserIDS); err != nil {
return err
}
rbacGlobal.RBACGlobal.RBACUsersIDs = make([]string, 0, len(rbacUserIDS))
for _, rbacUsers := range rbacUserIDS {
isValid, err := gorpmapping.CheckSignature(rbacUsers, rbacUsers.Signature)
if err != nil {
return sdk.WrapError(err, "error when checking signature for rbac_global_users %d", rbacUsers.ID)
}
if !isValid {
log.Error(ctx, "rbac.getAllRBACGlobalUsers> rbac_global_users %d data corrupted", rbacUsers.ID)
continue
}
rbacGlobal.RBACGlobal.RBACUsersIDs = append(rbacGlobal.RBACGlobal.RBACUsersIDs, rbacUsers.RbacGlobalUserID)
}
return nil
}

func getAllRBACGlobalGroups(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
q := gorpmapping.NewQuery("SELECT * FROM rbac_global_groups WHERE rbac_global_id = $1").Args(rbacGlobal.ID)
var rbacGroupIDs []rbacGlobalGroup
if err := gorpmapping.GetAll(ctx, db, q, &rbacGroupIDs); err != nil {
return err
}
rbacGlobal.RBACGlobal.RBACGroupsIDs = make([]int64, 0, len(rbacGroupIDs))
for _, rbacGroups := range rbacGroupIDs {
isValid, err := gorpmapping.CheckSignature(rbacGroups, rbacGroups.Signature)
if err != nil {
return sdk.WrapError(err, "error when checking signature for rbac_global_groups %d", rbacGroups.ID)
}
if !isValid {
log.Error(ctx, "rbac.getAllRBACGlobalGroups> rbac_global_groups %d data corrupted", rbacGroups.ID)
continue
}
rbacGlobal.RBACGlobal.RBACGroupsIDs = append(rbacGlobal.RBACGlobal.RBACGroupsIDs, rbacGroups.RbacGlobalGroupID)
}
return nil
}
95 changes: 95 additions & 0 deletions engine/api/rbac/dao_rbac_project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package rbac

import (
"context"

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

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

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

for _, rbProjectID := range dbRP.RBACProjectsIDs {
if err := insertRbacProjectIdentifiers(ctx, db, dbRP.ID, rbProjectID); err != nil {
return err
}
}
for _, rbUserID := range dbRP.RBACUsersIDs {
if err := insertRbacProjectUser(ctx, db, dbRP.ID, rbUserID); err != nil {
return err
}
}
for _, rbGroupID := range dbRP.RBACGroupsIDs {
if err := insertRbacProjectGroup(ctx, db, dbRP.ID, rbGroupID); err != nil {
return err
}
}
return nil
}

func insertRbacProjectIdentifiers(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacParentID int64, projectID int64) error {
identifier := rbacProjectIdentifiers{
RbacProjectID: rbacParentID,
ProjectID: projectID,
}
if err := gorpmapping.InsertAndSign(ctx, db, &identifier); err != nil {
return err
}
return nil
}

func insertRbacProjectUser(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacProjectID int64, userID string) error {
rgu := rbacProjectUser{
RbacProjectID: rbacProjectID,
RbacProjectUserID: userID,
}
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
return err
}
return nil
}

func insertRbacProjectGroup(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacProjectID int64, groupID int64) error {
rgu := rbacProjectGroup{
RbacProjectID: rbacProjectID,
RbacProjectGroupID: groupID,
}
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
return err
}
return nil
}

func getAllRbacProjects(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query) ([]rbacProject, error) {
var rbacProjects []rbacProject
if err := gorpmapping.GetAll(ctx, db, q, &rbacProjects); err != nil {
return nil, err
}

projectsFiltered := make([]rbacProject, 0, len(rbacProjects))
for _, projectDatas := range rbacProjects {
isValid, err := gorpmapping.CheckSignature(projectDatas, projectDatas.Signature)
if err != nil {
return nil, sdk.WrapError(err, "error when checking signature for rbac_project %d", projectDatas.ID)
}
if !isValid {
log.Error(ctx, "rbac.getAllRbacProjects> rbac_project %d data corrupted", projectDatas.ID)
continue
}
projectsFiltered = append(projectsFiltered, projectDatas)
}
return projectsFiltered, nil
}

func loadRbacProjectsByRoleAndIDs(ctx context.Context, db gorp.SqlExecutor, role string, rbacProjectIDs []int64) ([]rbacProject, error) {
q := gorpmapping.NewQuery(`SELECT * from rbac_project WHERE role = $1 AND id = ANY($2)`).Args(role, pq.Int64Array(rbacProjectIDs))
return getAllRbacProjects(ctx, db, q)
}
64 changes: 64 additions & 0 deletions engine/api/rbac/dao_rbac_project_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package rbac

import (
"context"

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

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

func loadRbacProjectGroupsByUserID(ctx context.Context, db gorp.SqlExecutor, userID string) ([]rbacProjectGroup, error) {
groups, err := group.LoadAllByUserID(ctx, db, userID)
if err != nil {
return nil, err
}
groupIDs := make([]int64, 0, len(groups))
for _, g := range groups {
groupIDs = append(groupIDs, g.ID)
}
return loadRbacProjectGroupsByGroupIDs(ctx, db, groupIDs)
}

func loadRbacProjectGroupsByGroupIDs(ctx context.Context, db gorp.SqlExecutor, groupIDs []int64) ([]rbacProjectGroup, error) {
q := gorpmapping.NewQuery("SELECT * FROM rbac_project_groups WHERE group_id = ANY ($1)").Args(pq.Int64Array(groupIDs))
return getAllRBACProjectGroups(ctx, db, q)
}

func loadRBACProjectGroups(ctx context.Context, db gorp.SqlExecutor, rbacProject *rbacProject) error {
q := gorpmapping.NewQuery("SELECT * FROM rbac_project_groups WHERE rbac_project_id = $1").Args(rbacProject.ID)
rbacProjectGroups, err := getAllRBACProjectGroups(ctx, db, q)
if err != nil {
return err
}
rbacProject.RBACProject.RBACGroupsIDs = make([]int64, 0, len(rbacProjectGroups))
for _, g := range rbacProjectGroups {
rbacProject.RBACProject.RBACGroupsIDs = append(rbacProject.RBACProject.RBACGroupsIDs, g.RbacProjectGroupID)
}
return nil
}

func getAllRBACProjectGroups(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query) ([]rbacProjectGroup, error) {
var rbacGroupIDs []rbacProjectGroup
if err := gorpmapping.GetAll(ctx, db, q, &rbacGroupIDs); err != nil {
return nil, err
}

groupsFiltered := make([]rbacProjectGroup, 0, len(rbacGroupIDs))
for _, rbacGroups := range rbacGroupIDs {
isValid, err := gorpmapping.CheckSignature(rbacGroups, rbacGroups.Signature)
if err != nil {
return nil, sdk.WrapError(err, "error when checking signature for rbac_project_groups %d", rbacGroups.ID)
}
if !isValid {
log.Error(ctx, "rbac.getAllRBACProjectGroups> rbac_project_groups %d data corrupted", rbacGroups.ID)
continue
}
groupsFiltered = append(groupsFiltered, rbacGroups)
}
return groupsFiltered, nil
}
Loading

0 comments on commit fce41f8

Please sign in to comment.