Skip to content

Commit e7be6f8

Browse files
committed
Add webhook integration functionality and enhance project controller
Signed-off-by: Rafi <refaei.shikho@hotmail.com>
1 parent db9c22a commit e7be6f8

7 files changed

Lines changed: 72 additions & 14 deletions

File tree

cmd/devguard/api/api.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ func BuildRouter(db core.DB) *echo.Echo {
403403
policyRepository := repositories.NewPolicyRepository(db)
404404
licenseOverwriteRepository := repositories.NewLicenseOverwriteRepository(db)
405405

406+
webhookRepository := repositories.NewWebhookRepository(db)
407+
406408
dependencyVulnService := vuln.NewService(dependencyVulnRepository, vulnEventRepository, assetRepository, cveRepository, orgRepository, projectRepository, thirdPartyIntegration, assetVersionRepository)
407409
firstPartyVulnService := vuln.NewFirstPartyVulnService(firstPartyVulnRepository, vulnEventRepository, assetRepository)
408410
projectService := project.NewService(projectRepository, assetRepository)
@@ -427,7 +429,7 @@ func BuildRouter(db core.DB) *echo.Echo {
427429
policyController := compliance.NewPolicyController(policyRepository, projectRepository)
428430
patController := pat.NewHTTPController(patRepository)
429431
orgController := org.NewHTTPController(orgRepository, orgService, casbinRBACProvider, projectService, invitationRepository)
430-
projectController := project.NewHTTPController(projectRepository, assetRepository, projectService)
432+
projectController := project.NewHTTPController(projectRepository, assetRepository, projectService, webhookRepository)
431433
assetController := asset.NewHTTPController(assetRepository, assetVersionRepository, assetService, dependencyVulnService, statisticsService)
432434

433435
scanController := scan.NewHTTPController(db, cveRepository, componentRepository, assetRepository, assetVersionRepository, assetVersionService, statisticsService, dependencyVulnService)
@@ -570,6 +572,10 @@ func BuildRouter(db core.DB) *echo.Echo {
570572
projectRouter := organizationRouter.Group("/projects/:projectSlug", projectAccessControl(projectService, "project", core.ActionRead))
571573
projectRouter.GET("/", projectController.Read)
572574

575+
projectRouter.POST("/integrations/webhook/test-and-save/", integrationController.TestAndSaveWebhookIntegration, neededScope([]string{"manage"}))
576+
projectRouter.PUT("/integrations/webhook/test-and-save/", integrationController.UpdateWebhookIntegration, neededScope([]string{"manage"}))
577+
projectRouter.DELETE("/integrations/webhook/:id/", integrationController.DeleteWebhookIntegration, neededScope([]string{"manage"}))
578+
573579
projectRouter.PUT("/policies/:policyID/", policyController.EnablePolicyForProject, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectProject, core.ActionUpdate))
574580
projectRouter.DELETE("/policies/:policyID/", policyController.DisablePolicyForProject, neededScope([]string{"manage"}), projectScopedRBAC(core.ObjectProject, core.ActionDelete))
575581

internal/core/common_interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ type WebhookIntegrationRepository interface {
331331
FindByOrgIDAndProjectID(orgID uuid.UUID, projectID uuid.UUID) ([]models.WebhookIntegration, error)
332332
Delete(tx DB, id uuid.UUID) error
333333
GetClientByIntegrationID(integrationID uuid.UUID) (models.WebhookIntegration, error)
334+
GetProjectWebhooks(orgID uuid.UUID, projectID uuid.UUID) ([]models.WebhookIntegration, error)
334335
}
335336

336337
type GitlabIntegrationRepository interface {

internal/core/integrations/webhook/webhook_integration.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ func (w *WebhookIntegration) Update(ctx core.Context) error {
6868
if err != nil {
6969
return ctx.JSON(400, "invalid id format")
7070
}
71-
webhookIntegration := &models.WebhookIntegration{
7271

72+
oldWebhookIntegration, err := w.webhookRepository.GetClientByIntegrationID(uuidID)
73+
if err != nil {
74+
slog.Error("failed to get webhook integration by ID", "err", err)
75+
return ctx.JSON(500, "failed to get webhook integration")
76+
}
77+
78+
webhookIntegration := &models.WebhookIntegration{
7379
Model: models.Model{
7480
ID: uuidID,
7581
},
@@ -79,7 +85,8 @@ func (w *WebhookIntegration) Update(ctx core.Context) error {
7985
Secret: &data.Secret,
8086
SbomEnabled: data.SbomEnabled,
8187
VulnEnabled: data.VulnEnabled,
82-
OrgID: core.GetOrg(ctx).GetID(),
88+
OrgID: oldWebhookIntegration.OrgID,
89+
ProjectID: oldWebhookIntegration.ProjectID,
8390
}
8491

8592
if err := w.webhookRepository.Save(nil, webhookIntegration); err != nil {
@@ -104,7 +111,6 @@ func (w *WebhookIntegration) TestAndSave(ctx core.Context) error {
104111
Secret string `json:"secret"`
105112
SbomEnabled bool `json:"sbomEnabled"`
106113
VulnEnabled bool `json:"vulnEnabled"`
107-
ProjectID string `json:"projectId"` // Optional, can be empty if not associated with a project
108114
}
109115

110116
if err := ctx.Bind(&data); err != nil {
@@ -115,12 +121,11 @@ func (w *WebhookIntegration) TestAndSave(ctx core.Context) error {
115121
}
116122

117123
var projectID *uuid.UUID
118-
if data.ProjectID != "" {
119-
parsedProjectID, err := uuid.Parse(data.ProjectID)
120-
if err != nil {
121-
return ctx.JSON(400, "invalid project ID format")
122-
}
123-
projectID = &parsedProjectID
124+
125+
ok := core.HasProject(ctx)
126+
if ok {
127+
projID := core.GetProject(ctx).GetID()
128+
projectID = &projID
124129
}
125130

126131
webhookIntegration := &models.WebhookIntegration{

internal/core/project/project_controller.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"encoding/json"
2020
"fmt"
2121

22+
"github.com/l3montree-dev/devguard/internal/common"
2223
"github.com/l3montree-dev/devguard/internal/core"
2324
"github.com/l3montree-dev/devguard/internal/database/models"
2425
"github.com/l3montree-dev/devguard/internal/utils"
@@ -31,13 +32,15 @@ type controller struct {
3132
projectRepository core.ProjectRepository
3233
assetRepository core.AssetRepository
3334
projectService core.ProjectService
35+
webhookRepository core.WebhookIntegrationRepository
3436
}
3537

36-
func NewHTTPController(repository core.ProjectRepository, assetRepository core.AssetRepository, projectService core.ProjectService) *controller {
38+
func NewHTTPController(repository core.ProjectRepository, assetRepository core.AssetRepository, projectService core.ProjectService, webhookRepository core.WebhookIntegrationRepository) *controller {
3739
return &controller{
3840
projectRepository: repository,
3941
assetRepository: assetRepository,
4042
projectService: projectService,
43+
webhookRepository: webhookRepository,
4144
}
4245
}
4346

@@ -268,14 +271,44 @@ func (projectController *controller) Read(c core.Context) error {
268271
return err
269272
}
270273

274+
//get the webhooks
275+
webhooks, err := projectController.getWebhooks(c)
276+
if err != nil {
277+
return echo.NewHTTPError(500, "could not fetch webhooks").WithInternal(err)
278+
}
279+
271280
resp := projectDetailsDTO{
272281
ProjectDTO: fromModel(project),
273282
Members: members,
283+
Webhooks: webhooks,
274284
}
275285

276286
return c.JSON(200, resp)
277287
}
278288

289+
func (projectController *controller) getWebhooks(c core.Context) ([]common.WebhookIntegrationDTO, error) {
290+
291+
orgID := core.GetOrg(c).GetID()
292+
projectID := core.GetProject(c).GetID()
293+
294+
webhooks, err := projectController.webhookRepository.GetProjectWebhooks(orgID, projectID)
295+
if err != nil {
296+
return nil, fmt.Errorf("could not fetch webhooks: %w", err)
297+
}
298+
299+
return utils.Map(webhooks, func(w models.WebhookIntegration) common.WebhookIntegrationDTO {
300+
return common.WebhookIntegrationDTO{
301+
ID: w.ID.String(),
302+
Name: *w.Name,
303+
Description: *w.Description,
304+
URL: w.URL,
305+
Secret: *w.Secret,
306+
SbomEnabled: w.SbomEnabled,
307+
VulnEnabled: w.VulnEnabled,
308+
}
309+
}), nil
310+
}
311+
279312
func (projectController *controller) List(c core.Context) error {
280313
// get all projects the user has at least read access to - might be public projects as well
281314
projects, err := projectController.projectService.ListAllowedProjects(c)

internal/core/project/project_dto.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package project
1818
import (
1919
"github.com/google/uuid"
2020
"github.com/gosimple/slug"
21+
"github.com/l3montree-dev/devguard/internal/common"
2122
"github.com/l3montree-dev/devguard/internal/core"
2223
"github.com/l3montree-dev/devguard/internal/database/models"
2324
"github.com/l3montree-dev/devguard/internal/utils"
@@ -128,7 +129,8 @@ type ProjectDTO struct {
128129

129130
type projectDetailsDTO struct {
130131
ProjectDTO
131-
Members []core.User `json:"members"`
132+
Members []core.User `json:"members"`
133+
Webhooks []common.WebhookIntegrationDTO `json:"webhooks"`
132134
}
133135

134136
func fromModel(project models.Project) ProjectDTO {

internal/database/repositories/org_repository.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func (g *orgRepository) Save(tx core.DB, org *models.Org) error {
6868

6969
func (g *orgRepository) ReadBySlug(slug string) (models.Org, error) {
7070
var t models.Org
71-
err := g.db.Model(models.Org{}).Preload("GithubAppInstallations").Preload("JiraIntegrations").Preload("GitLabIntegrations").Preload("Webhooks").Where("slug = ?", slug).First(&t).Error
71+
err := g.db.Model(models.Org{}).Preload("GithubAppInstallations").Preload("JiraIntegrations").Preload("GitLabIntegrations").Preload("Webhooks", "project_id IS NULL").Where("slug = ?", slug).First(&t).Error
7272
return t, err
7373
}
7474

internal/database/repositories/webhook_repository.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,18 @@ func NewWebhookRepository(db core.DB) *webhookRepository {
2727
func (r *webhookRepository) FindByOrgIDAndProjectID(orgID uuid.UUID, projectID uuid.UUID) ([]models.WebhookIntegration, error) {
2828
var integrations []models.WebhookIntegration
2929

30-
query := r.db.Where("organization_id = ? AND project_id IS NULL", orgID).Or("organization_id = ? AND project_id = ?", orgID, projectID)
30+
query := r.db.Where("org_id = ? AND project_id IS NULL", orgID).Or("org_id = ? AND project_id = ?", orgID, projectID)
31+
32+
if err := query.Find(&integrations).Error; err != nil {
33+
return nil, err
34+
}
35+
36+
return integrations, nil
37+
}
38+
func (r *webhookRepository) GetProjectWebhooks(orgID uuid.UUID, projectID uuid.UUID) ([]models.WebhookIntegration, error) {
39+
var integrations []models.WebhookIntegration
40+
41+
query := r.db.Where("org_id = ? AND project_id = ?", orgID, projectID)
3142

3243
if err := query.Find(&integrations).Error; err != nil {
3344
return nil, err

0 commit comments

Comments
 (0)