Skip to content

Commit fce41f8

Browse files
authored
feat: add rbac middleware system (#6103)
1 parent 1698e23 commit fce41f8

29 files changed

+1556
-20
lines changed

engine/api/api_routes.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (api *API) InitRouter() {
2929
api.Router.SetHeaderFunc = service.DefaultHeaders
3030
api.Router.Middlewares = append(api.Router.Middlewares, api.tracingMiddleware, api.jwtMiddleware)
3131
api.Router.DefaultAuthMiddleware = api.authMiddleware
32-
api.Router.PostAuthMiddlewares = append(api.Router.PostAuthMiddlewares, api.xsrfMiddleware, api.maintenanceMiddleware)
32+
api.Router.PostAuthMiddlewares = append(api.Router.PostAuthMiddlewares, api.xsrfMiddleware, api.maintenanceMiddleware, api.rbacMiddleware)
3333
api.Router.PostMiddlewares = append(api.Router.PostMiddlewares, service.TracingPostMiddleware)
3434

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

452+
r.Handle("/v2/project/{projectKey}/vcs", nil, r.POSTv2(api.postVCSOnProjectHandler))
453+
452454
//Not Found handler
453455
r.Mux.NotFoundHandler = http.HandlerFunc(r.NotFoundHandler)
454456

engine/api/rbac/dao_rbac.go

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package rbac
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/go-gorp/gorp"
8+
"github.com/rockbears/log"
9+
10+
"github.com/ovh/cds/engine/api/database/gorpmapping"
11+
"github.com/ovh/cds/engine/gorpmapper"
12+
"github.com/ovh/cds/sdk"
13+
)
14+
15+
func LoadRbacByName(ctx context.Context, db gorp.SqlExecutor, name string, opts ...LoadOptionFunc) (sdk.RBAC, error) {
16+
query := `SELECT * FROM rbac WHERE name = $1`
17+
return get(ctx, db, gorpmapping.NewQuery(query).Args(name), opts...)
18+
}
19+
20+
// Insert a RBAC permission in database
21+
func Insert(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rb *sdk.RBAC) error {
22+
if err := sdk.IsValidRbac(rb); err != nil {
23+
return err
24+
}
25+
if rb.UUID == "" {
26+
rb.UUID = sdk.UUID()
27+
}
28+
if rb.Created.IsZero() {
29+
rb.Created = time.Now()
30+
}
31+
rb.LastModified = time.Now()
32+
dbRb := rbac{RBAC: *rb}
33+
if err := gorpmapping.InsertAndSign(ctx, db, &dbRb); err != nil {
34+
return err
35+
}
36+
37+
for i := range rb.Globals {
38+
dbRbGlobal := rbacGlobal{
39+
RbacUUID: dbRb.UUID,
40+
RBACGlobal: rb.Globals[i],
41+
}
42+
if err := insertRbacGlobal(ctx, db, &dbRbGlobal); err != nil {
43+
return err
44+
}
45+
}
46+
for i := range rb.Projects {
47+
dbRbProject := rbacProject{
48+
RbacUUID: dbRb.UUID,
49+
RBACProject: rb.Projects[i],
50+
}
51+
if err := insertRbacProject(ctx, db, &dbRbProject); err != nil {
52+
return err
53+
}
54+
}
55+
*rb = dbRb.RBAC
56+
return nil
57+
}
58+
59+
func Update(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rb *sdk.RBAC) error {
60+
if err := Delete(ctx, db, *rb); err != nil {
61+
return err
62+
}
63+
return Insert(ctx, db, rb)
64+
}
65+
66+
func Delete(_ context.Context, db gorpmapper.SqlExecutorWithTx, rb sdk.RBAC) error {
67+
dbRb := rbac{RBAC: rb}
68+
if err := gorpmapping.Delete(db, &dbRb); err != nil {
69+
return err
70+
}
71+
return nil
72+
}
73+
74+
func get(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query, opts ...LoadOptionFunc) (sdk.RBAC, error) {
75+
var r sdk.RBAC
76+
var rbacDB rbac
77+
found, err := gorpmapping.Get(ctx, db, q, &rbacDB)
78+
if err != nil {
79+
return r, err
80+
}
81+
if !found {
82+
return r, sdk.WithStack(sdk.ErrNotFound)
83+
}
84+
85+
isValid, err := gorpmapping.CheckSignature(rbacDB, rbacDB.Signature)
86+
if err != nil {
87+
return r, sdk.WrapError(err, "error when checking signature for rbac %s", rbacDB.UUID)
88+
}
89+
if !isValid {
90+
log.Error(ctx, "rbac.get> rbac %s (%s) data corrupted", rbacDB.Name, rbacDB.UUID)
91+
return r, sdk.WithStack(sdk.ErrNotFound)
92+
}
93+
for _, f := range opts {
94+
if err := f(ctx, db, &rbacDB); err != nil {
95+
return r, err
96+
}
97+
}
98+
r = rbacDB.RBAC
99+
return r, nil
100+
}

engine/api/rbac/dao_rbac_global.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package rbac
2+
3+
import (
4+
"context"
5+
6+
"github.com/go-gorp/gorp"
7+
"github.com/rockbears/log"
8+
9+
"github.com/ovh/cds/engine/api/database/gorpmapping"
10+
"github.com/ovh/cds/engine/gorpmapper"
11+
"github.com/ovh/cds/sdk"
12+
)
13+
14+
func insertRbacGlobal(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rg *rbacGlobal) error {
15+
if err := gorpmapping.InsertAndSign(ctx, db, rg); err != nil {
16+
return err
17+
}
18+
19+
for _, userID := range rg.RBACUsersIDs {
20+
if err := insertRbacGlobalUser(ctx, db, rg.ID, userID); err != nil {
21+
return err
22+
}
23+
}
24+
for _, groupID := range rg.RBACGroupsIDs {
25+
if err := insertRbacGlobalGroup(ctx, db, rg.ID, groupID); err != nil {
26+
return err
27+
}
28+
}
29+
return nil
30+
}
31+
32+
func insertRbacGlobalUser(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacGlobalID int64, userID string) error {
33+
rgu := rbacGlobalUser{
34+
RbacGlobalID: rbacGlobalID,
35+
RbacGlobalUserID: userID,
36+
}
37+
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
38+
return err
39+
}
40+
return nil
41+
}
42+
43+
func insertRbacGlobalGroup(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacGlobalID int64, groupID int64) error {
44+
rgu := rbacGlobalGroup{
45+
RbacGlobalID: rbacGlobalID,
46+
RbacGlobalGroupID: groupID,
47+
}
48+
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
49+
return err
50+
}
51+
return nil
52+
}
53+
54+
func getAllRBACGlobalUsers(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
55+
q := gorpmapping.NewQuery("SELECT * FROM rbac_global_users WHERE rbac_global_id = $1").Args(rbacGlobal.ID)
56+
var rbacUserIDS []rbacGlobalUser
57+
if err := gorpmapping.GetAll(ctx, db, q, &rbacUserIDS); err != nil {
58+
return err
59+
}
60+
rbacGlobal.RBACGlobal.RBACUsersIDs = make([]string, 0, len(rbacUserIDS))
61+
for _, rbacUsers := range rbacUserIDS {
62+
isValid, err := gorpmapping.CheckSignature(rbacUsers, rbacUsers.Signature)
63+
if err != nil {
64+
return sdk.WrapError(err, "error when checking signature for rbac_global_users %d", rbacUsers.ID)
65+
}
66+
if !isValid {
67+
log.Error(ctx, "rbac.getAllRBACGlobalUsers> rbac_global_users %d data corrupted", rbacUsers.ID)
68+
continue
69+
}
70+
rbacGlobal.RBACGlobal.RBACUsersIDs = append(rbacGlobal.RBACGlobal.RBACUsersIDs, rbacUsers.RbacGlobalUserID)
71+
}
72+
return nil
73+
}
74+
75+
func getAllRBACGlobalGroups(ctx context.Context, db gorp.SqlExecutor, rbacGlobal *rbacGlobal) error {
76+
q := gorpmapping.NewQuery("SELECT * FROM rbac_global_groups WHERE rbac_global_id = $1").Args(rbacGlobal.ID)
77+
var rbacGroupIDs []rbacGlobalGroup
78+
if err := gorpmapping.GetAll(ctx, db, q, &rbacGroupIDs); err != nil {
79+
return err
80+
}
81+
rbacGlobal.RBACGlobal.RBACGroupsIDs = make([]int64, 0, len(rbacGroupIDs))
82+
for _, rbacGroups := range rbacGroupIDs {
83+
isValid, err := gorpmapping.CheckSignature(rbacGroups, rbacGroups.Signature)
84+
if err != nil {
85+
return sdk.WrapError(err, "error when checking signature for rbac_global_groups %d", rbacGroups.ID)
86+
}
87+
if !isValid {
88+
log.Error(ctx, "rbac.getAllRBACGlobalGroups> rbac_global_groups %d data corrupted", rbacGroups.ID)
89+
continue
90+
}
91+
rbacGlobal.RBACGlobal.RBACGroupsIDs = append(rbacGlobal.RBACGlobal.RBACGroupsIDs, rbacGroups.RbacGlobalGroupID)
92+
}
93+
return nil
94+
}

engine/api/rbac/dao_rbac_project.go

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package rbac
2+
3+
import (
4+
"context"
5+
6+
"github.com/go-gorp/gorp"
7+
"github.com/lib/pq"
8+
"github.com/rockbears/log"
9+
10+
"github.com/ovh/cds/engine/api/database/gorpmapping"
11+
"github.com/ovh/cds/engine/gorpmapper"
12+
"github.com/ovh/cds/sdk"
13+
)
14+
15+
func insertRbacProject(ctx context.Context, db gorpmapper.SqlExecutorWithTx, dbRP *rbacProject) error {
16+
if err := gorpmapping.InsertAndSign(ctx, db, dbRP); err != nil {
17+
return err
18+
}
19+
20+
for _, rbProjectID := range dbRP.RBACProjectsIDs {
21+
if err := insertRbacProjectIdentifiers(ctx, db, dbRP.ID, rbProjectID); err != nil {
22+
return err
23+
}
24+
}
25+
for _, rbUserID := range dbRP.RBACUsersIDs {
26+
if err := insertRbacProjectUser(ctx, db, dbRP.ID, rbUserID); err != nil {
27+
return err
28+
}
29+
}
30+
for _, rbGroupID := range dbRP.RBACGroupsIDs {
31+
if err := insertRbacProjectGroup(ctx, db, dbRP.ID, rbGroupID); err != nil {
32+
return err
33+
}
34+
}
35+
return nil
36+
}
37+
38+
func insertRbacProjectIdentifiers(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacParentID int64, projectID int64) error {
39+
identifier := rbacProjectIdentifiers{
40+
RbacProjectID: rbacParentID,
41+
ProjectID: projectID,
42+
}
43+
if err := gorpmapping.InsertAndSign(ctx, db, &identifier); err != nil {
44+
return err
45+
}
46+
return nil
47+
}
48+
49+
func insertRbacProjectUser(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacProjectID int64, userID string) error {
50+
rgu := rbacProjectUser{
51+
RbacProjectID: rbacProjectID,
52+
RbacProjectUserID: userID,
53+
}
54+
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
55+
return err
56+
}
57+
return nil
58+
}
59+
60+
func insertRbacProjectGroup(ctx context.Context, db gorpmapper.SqlExecutorWithTx, rbacProjectID int64, groupID int64) error {
61+
rgu := rbacProjectGroup{
62+
RbacProjectID: rbacProjectID,
63+
RbacProjectGroupID: groupID,
64+
}
65+
if err := gorpmapping.InsertAndSign(ctx, db, &rgu); err != nil {
66+
return err
67+
}
68+
return nil
69+
}
70+
71+
func getAllRbacProjects(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query) ([]rbacProject, error) {
72+
var rbacProjects []rbacProject
73+
if err := gorpmapping.GetAll(ctx, db, q, &rbacProjects); err != nil {
74+
return nil, err
75+
}
76+
77+
projectsFiltered := make([]rbacProject, 0, len(rbacProjects))
78+
for _, projectDatas := range rbacProjects {
79+
isValid, err := gorpmapping.CheckSignature(projectDatas, projectDatas.Signature)
80+
if err != nil {
81+
return nil, sdk.WrapError(err, "error when checking signature for rbac_project %d", projectDatas.ID)
82+
}
83+
if !isValid {
84+
log.Error(ctx, "rbac.getAllRbacProjects> rbac_project %d data corrupted", projectDatas.ID)
85+
continue
86+
}
87+
projectsFiltered = append(projectsFiltered, projectDatas)
88+
}
89+
return projectsFiltered, nil
90+
}
91+
92+
func loadRbacProjectsByRoleAndIDs(ctx context.Context, db gorp.SqlExecutor, role string, rbacProjectIDs []int64) ([]rbacProject, error) {
93+
q := gorpmapping.NewQuery(`SELECT * from rbac_project WHERE role = $1 AND id = ANY($2)`).Args(role, pq.Int64Array(rbacProjectIDs))
94+
return getAllRbacProjects(ctx, db, q)
95+
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package rbac
2+
3+
import (
4+
"context"
5+
6+
"github.com/go-gorp/gorp"
7+
"github.com/lib/pq"
8+
"github.com/rockbears/log"
9+
10+
"github.com/ovh/cds/engine/api/database/gorpmapping"
11+
"github.com/ovh/cds/engine/api/group"
12+
"github.com/ovh/cds/sdk"
13+
)
14+
15+
func loadRbacProjectGroupsByUserID(ctx context.Context, db gorp.SqlExecutor, userID string) ([]rbacProjectGroup, error) {
16+
groups, err := group.LoadAllByUserID(ctx, db, userID)
17+
if err != nil {
18+
return nil, err
19+
}
20+
groupIDs := make([]int64, 0, len(groups))
21+
for _, g := range groups {
22+
groupIDs = append(groupIDs, g.ID)
23+
}
24+
return loadRbacProjectGroupsByGroupIDs(ctx, db, groupIDs)
25+
}
26+
27+
func loadRbacProjectGroupsByGroupIDs(ctx context.Context, db gorp.SqlExecutor, groupIDs []int64) ([]rbacProjectGroup, error) {
28+
q := gorpmapping.NewQuery("SELECT * FROM rbac_project_groups WHERE group_id = ANY ($1)").Args(pq.Int64Array(groupIDs))
29+
return getAllRBACProjectGroups(ctx, db, q)
30+
}
31+
32+
func loadRBACProjectGroups(ctx context.Context, db gorp.SqlExecutor, rbacProject *rbacProject) error {
33+
q := gorpmapping.NewQuery("SELECT * FROM rbac_project_groups WHERE rbac_project_id = $1").Args(rbacProject.ID)
34+
rbacProjectGroups, err := getAllRBACProjectGroups(ctx, db, q)
35+
if err != nil {
36+
return err
37+
}
38+
rbacProject.RBACProject.RBACGroupsIDs = make([]int64, 0, len(rbacProjectGroups))
39+
for _, g := range rbacProjectGroups {
40+
rbacProject.RBACProject.RBACGroupsIDs = append(rbacProject.RBACProject.RBACGroupsIDs, g.RbacProjectGroupID)
41+
}
42+
return nil
43+
}
44+
45+
func getAllRBACProjectGroups(ctx context.Context, db gorp.SqlExecutor, q gorpmapping.Query) ([]rbacProjectGroup, error) {
46+
var rbacGroupIDs []rbacProjectGroup
47+
if err := gorpmapping.GetAll(ctx, db, q, &rbacGroupIDs); err != nil {
48+
return nil, err
49+
}
50+
51+
groupsFiltered := make([]rbacProjectGroup, 0, len(rbacGroupIDs))
52+
for _, rbacGroups := range rbacGroupIDs {
53+
isValid, err := gorpmapping.CheckSignature(rbacGroups, rbacGroups.Signature)
54+
if err != nil {
55+
return nil, sdk.WrapError(err, "error when checking signature for rbac_project_groups %d", rbacGroups.ID)
56+
}
57+
if !isValid {
58+
log.Error(ctx, "rbac.getAllRBACProjectGroups> rbac_project_groups %d data corrupted", rbacGroups.ID)
59+
continue
60+
}
61+
groupsFiltered = append(groupsFiltered, rbacGroups)
62+
}
63+
return groupsFiltered, nil
64+
}

0 commit comments

Comments
 (0)