Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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]string `sql:"header"`
Payload map[string]string `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