Skip to content

Commit

Permalink
Merge pull request #41 from PSNAppz/improve_config
Browse files Browse the repository at this point in the history
Improve configuration
  • Loading branch information
PSNAppz authored Aug 9, 2023
2 parents 3faf0a1 + 606b9f4 commit 29a3f7b
Show file tree
Hide file tree
Showing 14 changed files with 375 additions and 304 deletions.
18 changes: 11 additions & 7 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@
"plugins": [{
"type": "monitor",
"settings": {
"verbose": true
},
"active_mode": false
"active_mode": false,
"verbose": true,
"publishers": [{
"type": "file",
"name": "monitor-log",
"directory": "."
}]
}
},
{
"type":"ratelimiter",
"settings":{
"active_mode": true,
"rate": 1,
"notify": [{
"publishers": [{
"type": "slack",
"token": "xoxb-198202255696-5682091092327-m8IHyjQEnO6FdIIslpzjq2nz",
"channelID": "C5UTW0J6N"
}]
},

"active_mode": true
}
}],
"methods": ["GET"],
"external": "/external",
Expand Down
5 changes: 2 additions & 3 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import (

// PluginConfig represents the configuration for a single plugin
type PluginConfig struct {
Type string `json:"type"`
Settings map[string]interface{} `json:"settings"`
ActiveMode bool `json:"active_mode"`
Type string `json:"type"`
Settings map[string]interface{} `json:"settings"`
}

// Endpoint represents an external API Endpoint and its corresponding internal endpoint
Expand Down
9 changes: 6 additions & 3 deletions pkg/middleware/intercept.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func Intercept(client *http.Client, method, url string, pluginConfigs []config.P
}

return func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close() // close initial request

// Execute passive plugins in separate goroutines
for _, p := range passivePlugins {
go p.Handle(r)
Expand All @@ -38,18 +40,19 @@ func Intercept(client *http.Client, method, url string, pluginConfigs []config.P
for _, p := range activePlugins {
err := p.Handle(r)
if err != nil {
log.Printf("Request blocked by plugin %s. Error %s", p.GetType(), err.Error())
log.Printf("Request blocked by plugin %s. Error %s", p.Type(), err.Error())
// If an active plugin returns an error, respond with an error message and status code
http.Error(w, "Request blocked by plugin: "+err.Error(), http.StatusForbidden)
return
}
}

defer r.Body.Close()
// route request to internal server
req, err := http.NewRequest(method, url, r.Body)
if err != nil {
panic(err)
}

resp, err := client.Do(req)
if err != nil {
panic(err)
Expand All @@ -68,7 +71,7 @@ func Intercept(client *http.Client, method, url string, pluginConfigs []config.P
func createPlugins(pluginConfigs []config.PluginConfig) ([]plugin.Plugin, error) {
plugins := make([]plugin.Plugin, len(pluginConfigs))
for i, pc := range pluginConfigs {
p, err := plugin.CreatePlugin(pc.Type, pc.Settings, pc.ActiveMode)
p, err := plugin.CreatePlugin(pc.Type, pc.Settings)
if err != nil {
return nil, err
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/middleware/intercept_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ func TestInterceptWithActivePlugins(t *testing.T) {
{
Type: "ipfilter",
Settings: map[string]interface{}{
"blacklist": []interface{}{"127.0.0.1"},
"whitelist": []interface{}{},
"active_mode": true,
"blacklist": []interface{}{"127.0.0.1"},
"whitelist": []interface{}{},
},
ActiveMode: true,
},
}
// create external endpoint (or client facing endpoint)
Expand Down
11 changes: 5 additions & 6 deletions pkg/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import (

// Plugin is an interface that any plugin should implement.
type Plugin interface {
Handle(r *http.Request) error
GetType() string
GetSettings() map[string]interface{}
Type() string
IsActiveMode() bool
Notify(message string)
Handle(r *http.Request) error
}

// PluginFactory is a function that creates a Plugin.
type PluginFactory func(settings map[string]interface{}, activeMode bool) Plugin
type PluginFactory func(settings map[string]interface{}) Plugin

// pluginRegistry holds a map of plugin types to factory functions.
var pluginRegistry = make(map[string]PluginFactory)
Expand All @@ -26,10 +25,10 @@ func RegisterPlugin(typeStr string, factory PluginFactory) {
}

// CreatePlugin creates a new Plugin of the given type with the given settings.
func CreatePlugin(typeStr string, settings map[string]interface{}, activeMode bool) (Plugin, error) {
func CreatePlugin(typeStr string, settings map[string]interface{}) (Plugin, error) {
factory, exists := pluginRegistry[typeStr]
if !exists {
return nil, fmt.Errorf("plugin type %s not found in registry", typeStr)
}
return factory(settings, activeMode), nil
return factory(settings), nil
}
56 changes: 56 additions & 0 deletions pkg/publisher/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package publisher

import (
"fmt"
"os"
"path/filepath"
)

type FilePublisher struct {
file *os.File
}

func (f FilePublisher) Publish(message string) error {
lineBytes := []byte(fmt.Sprintf("%s\n", message))
_, err := f.file.Write(lineBytes)
return err
}

func (f FilePublisher) Type() string {
return "file"
}

func NewFilePublisher(settings map[string]interface{}) (*FilePublisher, error) {
name, ok := settings["name"]
if !ok {
return nil, fmt.Errorf("file must have a name")
}

nameStr, ok := name.(string)
if !ok {
return nil, fmt.Errorf("file name must be a string, found: %+v", name)
}

targetDir, ok := settings["directory"]
if !ok {
return nil, fmt.Errorf("target directory must be specified")
}

targetDirStr, ok := targetDir.(string)
if !ok {
return nil, fmt.Errorf("directory must be a string found %+v", targetDir)
}

absDir, err := filepath.Abs(targetDirStr)
if err != nil {
return nil, err
}

targetFilePath := filepath.Join(absDir, nameStr)
file, err := os.OpenFile(targetFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}

return &FilePublisher{file: file}, nil
}
71 changes: 71 additions & 0 deletions pkg/publisher/publisher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package publisher

import (
"fmt"
)

// Publisher is an interface that publishes messages to a data source
type Publisher interface {
Type() string
Publish(message string) error
}

func CreatePublishers(pluginSettings map[string]interface{}) ([]Publisher, error) {
publisherSettings, err := parsePublisherSettings(pluginSettings)
if err != nil {
return nil, err
}

publishers := []Publisher{}
for _, setting := range publisherSettings {
publisher, err := NewPublisher(setting)
if err != nil {
return nil, err

}
publishers = append(publishers, publisher)
}
return publishers, nil
}

func NewPublisher(setting map[string]interface{}) (Publisher, error) {
publisherType, ok := setting["type"]
if !ok {
return nil, fmt.Errorf("publish setting must specify a type, found %v", publisherType)
}

var publisher Publisher
var err error

switch publisherType {
case "slack":
publisher, err = NewSlackPublisher(setting)
case "file":
publisher, err = NewFilePublisher(setting)
}

return publisher, err
}

func parsePublisherSettings(settings map[string]interface{}) ([]map[string]interface{}, error) {
publisherList, ok := settings["publishers"]
if !ok {
return nil, nil
}

publisherInterfaceList, ok := publisherList.([]interface{})
if !ok {
return nil, fmt.Errorf("publisher list is incorrectly configured, found %+v", publisherList)
}

publisherSettings := []map[string]interface{}{}
for _, publisherInterface := range publisherInterfaceList {
publisherSetting, ok := publisherInterface.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("publisher settting is incorrectly configured, found %+v", publisherSetting)
}
publisherSettings = append(publisherSettings, publisherSetting)
}

return publisherSettings, nil
}
45 changes: 45 additions & 0 deletions pkg/publisher/slack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package publisher

import (
"fmt"

"github.com/slack-go/slack"
)

type SlackPublisher struct {
api *slack.Client
channelID string
}

func (s SlackPublisher) Type() string {
return "slack"
}

func (s SlackPublisher) Publish(message string) error {
_, _, err := s.api.PostMessage(s.channelID, slack.MsgOptionText(message, false))
return err
}

func NewSlackPublisher(settings map[string]interface{}) (*SlackPublisher, error) {
channelID, ok := settings["channelID"]
if !ok {
return nil, fmt.Errorf("channel ID is required to send Slack notification")
}

channelIDStr, ok := channelID.(string)
if !ok {
return nil, fmt.Errorf("channel ID must be a string, found: %+v", channelID)
}

token, ok := settings["token"]
if !ok {
return nil, fmt.Errorf("API token required to send Slack notifications")
}

tokenStr, ok := token.(string)
if !ok {
return nil, fmt.Errorf("API token must be a string, found: %+v", tokenStr)
}

return &SlackPublisher{api: slack.New(tokenStr, slack.OptionDebug(true)), channelID: channelIDStr}, nil
}
Loading

0 comments on commit 29a3f7b

Please sign in to comment.