Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: interact with plugins from service endpoint for least privilege #192

Merged
merged 4 commits into from
Aug 5, 2022
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
112 changes: 88 additions & 24 deletions kong/plugin_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ import (
type AbstractPluginService interface {
// Create creates a Plugin in Kong.
Create(ctx context.Context, plugin *Plugin) (*Plugin, error)
// CreateForService creates a Plugin in Kong.
CreateForService(ctx context.Context, serviceIDorName *string, plugin *Plugin) (*Plugin, error)
// Get fetches a Plugin in Kong.
Get(ctx context.Context, usernameOrID *string) (*Plugin, error)
// Update updates a Plugin in Kong
Update(ctx context.Context, plugin *Plugin) (*Plugin, error)
// UpdateForService updates a Plugin in Kong for a service
UpdateForService(ctx context.Context, serviceIDorName *string, plugin *Plugin) (*Plugin, error)
// Delete deletes a Plugin in Kong
Delete(ctx context.Context, usernameOrID *string) error
// DeleteForService deletes a Plugin in Kong
DeleteForService(ctx context.Context, serviceIDorName *string, pluginID *string) error
// List fetches a list of Plugins in Kong.
List(ctx context.Context, opt *ListOpt) ([]*Plugin, *ListOpt, error)
// ListAll fetches all Plugins in Kong.
Expand Down Expand Up @@ -97,17 +103,24 @@ func (s *PluginService) Create(ctx context.Context,
queryPath = queryPath + "/" + *plugin.ID
method = "PUT"
}
req, err := s.client.NewRequest(method, queryPath, nil, plugin)
if err != nil {
return nil, err
}
return s.sendRequest(ctx, plugin, queryPath, method)
}

var createdPlugin Plugin
_, err = s.client.Do(ctx, req, &createdPlugin)
if err != nil {
return nil, err
// CreateForService creates a Plugin in Kong at Service level.
// If an ID is specified, it will be used to
// create a plugin in Kong, otherwise an ID
// is auto-generated.
func (s *PluginService) CreateForService(ctx context.Context,
serviceIDorName *string, plugin *Plugin,
) (*Plugin, error) {
if isEmptyString(plugin.ID) {
return nil, fmt.Errorf("ID cannot be nil for Update operation")
}
return &createdPlugin, nil
if isEmptyString(serviceIDorName) {
return nil, fmt.Errorf("serviceIDorName cannot be nil")
}

return s.sendRequest(ctx, plugin, fmt.Sprintf("/services/%v/plugins", *serviceIDorName), "POST")
}

// Get fetches a Plugin in Kong.
Expand Down Expand Up @@ -141,34 +154,56 @@ func (s *PluginService) Update(ctx context.Context,
}

endpoint := fmt.Sprintf("/plugins/%v", *plugin.ID)
req, err := s.client.NewRequest("PATCH", endpoint, nil, plugin)
if err != nil {
return nil, err
}
return s.sendRequest(ctx, plugin, endpoint, "PATCH")
}

var updatedAPI Plugin
_, err = s.client.Do(ctx, req, &updatedAPI)
if err != nil {
return nil, err
// UpdateForService updates a Plugin in Kong at Service level.
func (s *PluginService) UpdateForService(ctx context.Context,
serviceIDorName *string, plugin *Plugin,
) (*Plugin, error) {
if isEmptyString(plugin.ID) {
return nil, fmt.Errorf("ID cannot be nil for Update operation")
}
if isEmptyString(serviceIDorName) {
return nil, fmt.Errorf("serviceIDorName cannot be nil")
}
return &updatedAPI, nil

endpoint := fmt.Sprintf("/services/%v/plugins/%v", *serviceIDorName, *plugin.ID)
return s.sendRequest(ctx, plugin, endpoint, "PATCH")
}

// Delete deletes a Plugin in Kong
func (s *PluginService) Delete(ctx context.Context,
usernameOrID *string,
pluginID *string,
) error {
if isEmptyString(usernameOrID) {
return fmt.Errorf("usernameOrID cannot be nil for Delete operation")
if isEmptyString(pluginID) {
return fmt.Errorf("pluginID cannot be nil for Delete operation")
}

endpoint := fmt.Sprintf("/plugins/%v", *usernameOrID)
req, err := s.client.NewRequest("DELETE", endpoint, nil, nil)
endpoint := fmt.Sprintf("/plugins/%v", *pluginID)
_, err := s.sendRequest(ctx, nil, endpoint, "DELETE")
if err != nil {
return err
}
return err
}

// DeleteForService deletes a Plugin in Kong at Service level.
func (s *PluginService) DeleteForService(ctx context.Context,
serviceIDorName *string, pluginID *string,
) error {
if isEmptyString(pluginID) {
return fmt.Errorf("plugin ID cannot be nil for Delete operation")
}
if isEmptyString(serviceIDorName) {
return fmt.Errorf("serviceIDorName cannot be nil")
}

_, err = s.client.Do(ctx, req, nil)
endpoint := fmt.Sprintf("/services/%v/plugins/%v", *serviceIDorName, *pluginID)
_, err := s.sendRequest(ctx, nil, endpoint, "DELETE")
if err != nil {
return err
}
return err
}

Expand Down Expand Up @@ -294,3 +329,32 @@ func (s *PluginService) ListAllForRoute(ctx context.Context,
}
return s.listAllByPath(ctx, "/routes/"+*routeID+"/plugins")
}

func (s *PluginService) sendRequest(ctx context.Context, plugin *Plugin, endpoint, method string) (*Plugin, error) {
var req *http.Request
var err error
if method == "DELETE" {
req, err = s.client.NewRequest(method, endpoint, nil, nil)
if err != nil {
return nil, err
}
} else {
req, err = s.client.NewRequest(method, endpoint, nil, plugin)
if err != nil {
return nil, err
}
}
var createdPlugin Plugin
if method == "DELETE" {
_, err = s.client.Do(ctx, req, nil)
if err != nil {
return nil, err
}
} else {
_, err = s.client.Do(ctx, req, &createdPlugin)
if err != nil {
return nil, err
}
}
return &createdPlugin, nil
}
40 changes: 40 additions & 0 deletions kong/plugin_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,46 @@ func TestPluginsService(T *testing.T) {

err = client.Plugins.Delete(defaultCtx, createdPlugin.ID)
assert.NoError(err)

service := &Service{
Name: String("fooWithPlugin"),
Host: String("upstream"),
Port: Int(42),
Path: String("/path"),
}
// Clean Data
err = client.Services.Delete(defaultCtx, service.Name)
assert.NoError(err)
// Test to create plugin from service endpoint
createdService, err := client.Services.Create(defaultCtx, service)
assert.NoError(err)

id = uuid.NewString()
pluginForService := &Plugin{
Name: String("key-auth"),
ID: String(id),
Config: Configuration{
"anonymous": "true",
},
}

createdPlugin, err = client.Plugins.CreateForService(defaultCtx, createdService.Name, pluginForService)
assert.NoError(err)
assert.NotNil(createdPlugin)
assert.Equal(id, *createdPlugin.ID)
assert.Equal("true", createdPlugin.Config["anonymous"])

createdPlugin.Config["anonymous"] = "false"
updatedPlugin, err := client.Plugins.UpdateForService(defaultCtx, createdService.Name, createdPlugin)
assert.NoError(err)
assert.NotNil(createdPlugin)
assert.Equal(id, *createdPlugin.ID)
assert.Equal("false", updatedPlugin.Config["anonymous"])

err = client.Plugins.DeleteForService(defaultCtx, createdService.Name, updatedPlugin.ID)
assert.NoError(err)

assert.NoError(client.Services.Delete(defaultCtx, createdService.ID))
}

func TestPluginWithTags(T *testing.T) {
Expand Down