Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ func InitializeApp() (*App, error) {
wire.Bind(new(notifier.SlackNotificationService), new(*notifier.SlackNotificationServiceImpl)),
repository.NewSlackNotificationRepositoryImpl,
wire.Bind(new(repository.SlackNotificationRepository), new(*repository.SlackNotificationRepositoryImpl)),
notifier.NewWebhookNotificationServiceImpl,
wire.Bind(new(notifier.WebhookNotificationService), new(*notifier.WebhookNotificationServiceImpl)),
repository.NewWebhookNotificationRepositoryImpl,
wire.Bind(new(repository.WebhookNotificationRepository), new(*repository.WebhookNotificationRepositoryImpl)),

notifier.NewNotificationConfigServiceImpl,
wire.Bind(new(notifier.NotificationConfigService), new(*notifier.NotificationConfigServiceImpl)),
Expand Down
148 changes: 140 additions & 8 deletions api/restHandler/NotificationRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,21 @@ import (
)

const (
SLACK_CONFIG_DELETE_SUCCESS_RESP = "Slack config deleted successfully."
SES_CONFIG_DELETE_SUCCESS_RESP = "SES config deleted successfully."
SMTP_CONFIG_DELETE_SUCCESS_RESP = "SMTP config deleted successfully."
SLACK_CONFIG_DELETE_SUCCESS_RESP = "Slack config deleted successfully."
WEBHOOK_CONFIG_DELETE_SUCCESS_RESP = "Webhook config deleted successfully."
SES_CONFIG_DELETE_SUCCESS_RESP = "SES config deleted successfully."
SMTP_CONFIG_DELETE_SUCCESS_RESP = "SMTP config deleted successfully."
)

type NotificationRestHandler interface {
SaveNotificationSettings(w http.ResponseWriter, r *http.Request)
UpdateNotificationSettings(w http.ResponseWriter, r *http.Request)

SaveNotificationChannelConfig(w http.ResponseWriter, r *http.Request)
FindSESConfig(w http.ResponseWriter, r *http.Request)
FindSlackConfig(w http.ResponseWriter, r *http.Request)
FindSMTPConfig(w http.ResponseWriter, r *http.Request)
FindWebhookConfig(w http.ResponseWriter, r *http.Request)
GetWebhookVariables(w http.ResponseWriter, r *http.Request)
FindAllNotificationConfig(w http.ResponseWriter, r *http.Request)
GetAllNotificationSettings(w http.ResponseWriter, r *http.Request)
DeleteNotificationSettings(w http.ResponseWriter, r *http.Request)
Expand All @@ -76,6 +78,7 @@ type NotificationRestHandlerImpl struct {
validator *validator.Validate
notificationService notifier.NotificationConfigService
slackService notifier.SlackNotificationService
webhookService notifier.WebhookNotificationService
sesService notifier.SESNotificationService
smtpService notifier.SMTPNotificationService
enforcer casbin.Enforcer
Expand All @@ -93,7 +96,7 @@ func NewNotificationRestHandlerImpl(dockerRegistryConfig pipeline.DockerRegistry
logger *zap.SugaredLogger, gitRegistryConfig pipeline.GitRegistryConfig,
dbConfigService pipeline.DbConfigService, userAuthService user.UserService,
validator *validator.Validate, notificationService notifier.NotificationConfigService,
slackService notifier.SlackNotificationService, sesService notifier.SESNotificationService, smtpService notifier.SMTPNotificationService,
slackService notifier.SlackNotificationService, webhookService notifier.WebhookNotificationService, sesService notifier.SESNotificationService, smtpService notifier.SMTPNotificationService,
enforcer casbin.Enforcer, teamService team.TeamService, environmentService cluster.EnvironmentService, pipelineBuilder pipeline.PipelineBuilder,
enforcerUtil rbac.EnforcerUtil) *NotificationRestHandlerImpl {
return &NotificationRestHandlerImpl{
Expand All @@ -105,6 +108,7 @@ func NewNotificationRestHandlerImpl(dockerRegistryConfig pipeline.DockerRegistry
validator: validator,
notificationService: notificationService,
slackService: slackService,
webhookService: webhookService,
sesService: sesService,
smtpService: smtpService,
enforcer: enforcer,
Expand Down Expand Up @@ -551,13 +555,46 @@ func (impl NotificationRestHandlerImpl) SaveNotificationChannelConfig(w http.Res
}
w.Header().Set("Content-Type", "application/json")
common.WriteJsonResp(w, nil, res, http.StatusOK)
} else if util.Webhook == channelReq.Channel {
var webhookReq *notifier.WebhookChannelConfig
err = json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(data))).Decode(&webhookReq)
if err != nil {
impl.logger.Errorw("request err, SaveNotificationChannelConfig", "err", err, "webhookReq", webhookReq)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

err = impl.validator.Struct(webhookReq)
if err != nil {
impl.logger.Errorw("validation err, SaveNotificationChannelConfig", "err", err, "webhookReq", webhookReq)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

// RBAC enforcer applying
token := r.Header.Get("token")
if ok := impl.enforcer.Enforce(token, casbin.ResourceNotification, casbin.ActionCreate, "*"); !ok {
response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized"))
return
}
//RBAC enforcer Ends

res, cErr := impl.webhookService.SaveOrEditNotificationConfig(*webhookReq.WebhookConfigDtos, userId)
if cErr != nil {
impl.logger.Errorw("service err, SaveNotificationChannelConfig", "err", err, "webhookReq", webhookReq)
common.WriteJsonResp(w, cErr, nil, http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
common.WriteJsonResp(w, nil, res, http.StatusOK)
}
}

type ChannelResponseDTO struct {
SlackConfigs []*notifier.SlackConfigDto `json:"slackConfigs"`
SESConfigs []*notifier.SESConfigDto `json:"sesConfigs"`
SMTPConfigs []*notifier.SMTPConfigDto `json:"smtpConfigs"`
SlackConfigs []*notifier.SlackConfigDto `json:"slackConfigs"`
WebhookConfigs []*notifier.WebhookConfigDto `json:"webhookConfigs"`
SESConfigs []*notifier.SESConfigDto `json:"sesConfigs"`
SMTPConfigs []*notifier.SMTPConfigDto `json:"smtpConfigs"`
}

func (impl NotificationRestHandlerImpl) FindAllNotificationConfig(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -607,6 +644,18 @@ func (impl NotificationRestHandlerImpl) FindAllNotificationConfig(w http.Respons
if pass {
channelsResponse.SlackConfigs = slackConfigs
}
webhookConfigs, fErr := impl.webhookService.FetchAllWebhookNotificationConfig()
if fErr != nil && fErr != pg.ErrNoRows {
impl.logger.Errorw("service err, FindAllNotificationConfig", "err", err)
common.WriteJsonResp(w, fErr, nil, http.StatusInternalServerError)
return
}
if webhookConfigs == nil {
webhookConfigs = make([]*notifier.WebhookConfigDto, 0)
}
if pass {
channelsResponse.WebhookConfigs = webhookConfigs
}
sesConfigs, fErr := impl.sesService.FetchAllSESNotificationConfig()
if fErr != nil && fErr != pg.ErrNoRows {
impl.logger.Errorw("service err, FindAllNotificationConfig", "err", err)
Expand Down Expand Up @@ -717,6 +766,47 @@ func (impl NotificationRestHandlerImpl) FindSMTPConfig(w http.ResponseWriter, r
w.Header().Set("Content-Type", "application/json")
common.WriteJsonResp(w, fErr, smtpConfig, http.StatusOK)
}
func (impl NotificationRestHandlerImpl) FindWebhookConfig(w http.ResponseWriter, r *http.Request) {
userId, err := impl.userAuthService.GetLoggedInUser(r)
if userId == 0 || err != nil {
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
if err != nil {
impl.logger.Errorw("request err, FindWebhookConfig", "err", err, "id", id)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

webhookConfig, fErr := impl.webhookService.FetchWebhookNotificationConfigById(id)
if fErr != nil && fErr != pg.ErrNoRows {
impl.logger.Errorw("service err, FindWebhookConfig, cannot find webhook config", "err", fErr, "id", id)
common.WriteJsonResp(w, fErr, nil, http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
common.WriteJsonResp(w, fErr, webhookConfig, http.StatusOK)
}
func (impl NotificationRestHandlerImpl) GetWebhookVariables(w http.ResponseWriter, r *http.Request) {
userId, err := impl.userAuthService.GetLoggedInUser(r)
if userId == 0 || err != nil {
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}

webhookVariables, fErr := impl.webhookService.GetWebhookVariables()
if fErr != nil && fErr != pg.ErrNoRows {
impl.logger.Errorw("service err, GetWebhookVariables, cannot find webhook Variables", "err", fErr)
common.WriteJsonResp(w, fErr, nil, http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
common.WriteJsonResp(w, fErr, webhookVariables, http.StatusOK)
}

func (impl NotificationRestHandlerImpl) RecipientListingSuggestion(w http.ResponseWriter, r *http.Request) {
userId, err := impl.userAuthService.GetLoggedInUser(r)
Expand Down Expand Up @@ -783,6 +873,17 @@ func (impl NotificationRestHandlerImpl) FindAllNotificationConfigAutocomplete(w
}
}

} else if cType == string(util.Webhook) {
if ok := impl.enforcer.Enforce(token, casbin.ResourceNotification, casbin.ActionGet, "*"); !ok {
response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized"))
return
}
channelsResponse, err = impl.webhookService.FetchAllWebhookNotificationConfigAutocomplete()
if err != nil && err != pg.ErrNoRows {
impl.logger.Errorw("service err, FindAllNotificationConfigAutocomplete", "err", err)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
} else if cType == string(util.SES) {
if ok := impl.enforcer.Enforce(token, casbin.ResourceNotification, casbin.ActionGet, "*"); !ok {
response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized"))
Expand Down Expand Up @@ -929,6 +1030,37 @@ func (impl NotificationRestHandlerImpl) DeleteNotificationChannelConfig(w http.R
return
}
common.WriteJsonResp(w, nil, SLACK_CONFIG_DELETE_SUCCESS_RESP, http.StatusOK)
} else if util.Webhook == channelReq.Channel {
var deleteReq *notifier.WebhookConfigDto
err = json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(data))).Decode(&deleteReq)
if err != nil {
impl.logger.Errorw("request err, DeleteNotificationChannelConfig", "err", err, "deleteReq", deleteReq)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

err = impl.validator.Struct(deleteReq)
if err != nil {
impl.logger.Errorw("validation err, DeleteNotificationChannelConfig", "err", err, "deleteReq", deleteReq)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

// RBAC enforcer applying
token := r.Header.Get("token")
if ok := impl.enforcer.Enforce(token, casbin.ResourceNotification, casbin.ActionCreate, "*"); !ok {
response.WriteResponse(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized"))
return
}
//RBAC enforcer Ends

cErr := impl.webhookService.DeleteNotificationConfig(deleteReq, userId)
if cErr != nil {
impl.logger.Errorw("service err, DeleteNotificationChannelConfig", "err", err, "deleteReq", deleteReq)
common.WriteJsonResp(w, cErr, nil, http.StatusInternalServerError)
return
}
common.WriteJsonResp(w, nil, WEBHOOK_CONFIG_DELETE_SUCCESS_RESP, http.StatusOK)
} else if util.SES == channelReq.Channel {
var deleteReq *notifier.SESConfigDto
err = json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(data))).Decode(&deleteReq)
Expand Down
7 changes: 7 additions & 0 deletions api/router/NotificationRouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ func (impl NotificationRouterImpl) InitNotificationRegRouter(configRouter *mux.R
configRouter.Path("/channel/smtp/{id}").
HandlerFunc(impl.notificationRestHandler.FindSMTPConfig).
Methods("GET")
configRouter.Path("/channel/webhook/{id}").
HandlerFunc(impl.notificationRestHandler.FindWebhookConfig).
Methods("GET")
configRouter.Path("/variables").
HandlerFunc(impl.notificationRestHandler.GetWebhookVariables).
Methods("GET")

configRouter.Path("/channel").
HandlerFunc(impl.notificationRestHandler.DeleteNotificationChannelConfig).
Methods("DELETE")
Expand Down
81 changes: 81 additions & 0 deletions internal/sql/repository/WebhookNotificationRepository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package repository

import (
"github.com/devtron-labs/devtron/pkg/sql"
"github.com/go-pg/pg"
)

type WebhookNotificationRepository interface {
FindOne(id int) (*WebhookConfig, error)
UpdateWebhookConfig(webhookConfig *WebhookConfig) (*WebhookConfig, error)
SaveWebhookConfig(webhookConfig *WebhookConfig) (*WebhookConfig, error)
FindAll() ([]WebhookConfig, error)
FindByName(value string) ([]WebhookConfig, error)
FindByIds(ids []*int) ([]*WebhookConfig, error)
MarkWebhookConfigDeleted(webhookConfig *WebhookConfig) error
}

type WebhookNotificationRepositoryImpl struct {
dbConnection *pg.DB
}

func NewWebhookNotificationRepositoryImpl(dbConnection *pg.DB) *WebhookNotificationRepositoryImpl {
return &WebhookNotificationRepositoryImpl{dbConnection: dbConnection}
}

type WebhookConfig struct {
tableName struct{} `sql:"webhook_config" pg:",discard_unknown_columns"`
Id int `sql:"id,pk"`
WebHookUrl string `sql:"web_hook_url"`
ConfigName string `sql:"config_name"`
Header map[string]interface{} `sql:"header"`
Payload map[string]interface{} `sql:"payload"`
Description string `sql:"description"`
OwnerId int32 `sql:"owner_id"`
Active bool `sql:"active"`
Deleted bool `sql:"deleted,notnull"`
sql.AuditLog
}

func (impl *WebhookNotificationRepositoryImpl) FindOne(id int) (*WebhookConfig, error) {
details := &WebhookConfig{}
err := impl.dbConnection.Model(details).Where("id = ?", id).
Where("deleted = ?", false).Select()

return details, err
}

func (impl *WebhookNotificationRepositoryImpl) FindAll() ([]WebhookConfig, error) {
var webhookConfigs []WebhookConfig
err := impl.dbConnection.Model(&webhookConfigs).
Where("deleted = ?", false).Select()
return webhookConfigs, err
}

func (impl *WebhookNotificationRepositoryImpl) UpdateWebhookConfig(webhookConfig *WebhookConfig) (*WebhookConfig, error) {
return webhookConfig, impl.dbConnection.Update(webhookConfig)
}

func (impl *WebhookNotificationRepositoryImpl) SaveWebhookConfig(webhookConfig *WebhookConfig) (*WebhookConfig, error) {
return webhookConfig, impl.dbConnection.Insert(webhookConfig)
}

func (impl *WebhookNotificationRepositoryImpl) FindByName(value string) ([]WebhookConfig, error) {
var webhookConfigs []WebhookConfig
err := impl.dbConnection.Model(&webhookConfigs).Where(`config_name like ?`, "%"+value+"%").
Where("deleted = ?", false).Select()
return webhookConfigs, err

}

func (repo *WebhookNotificationRepositoryImpl) FindByIds(ids []*int) ([]*WebhookConfig, error) {
var objects []*WebhookConfig
err := repo.dbConnection.Model(&objects).Where("id in (?)", pg.In(ids)).
Where("deleted = ?", false).Select()
return objects, err
}

func (impl *WebhookNotificationRepositoryImpl) MarkWebhookConfigDeleted(webhookConfig *WebhookConfig) error {
webhookConfig.Deleted = true
return impl.dbConnection.Update(webhookConfig)
}
Loading