Skip to content

Commit

Permalink
feat(api): EmailUser Alert Channel (v2) (#492)
Browse files Browse the repository at this point in the history
`NewAlertChannel` returns an instance of the
AlertChannelRaw struct with the provided Alert Channel
integration type, name and raw data as an `interface{}`.

**NOTE: This function must be used by any Alert Channel type.**

Basic usage: Initialize a new `EmailUserAlertChannel`
             struct, then use the new instance to do
	     CRUD operations
```
  client, err := api.NewClient("account")
  if err != nil {
    return err
  }

  emailAlertChan := api.NewAlertChannel("foo",
    api.EmailUserAlertChannel,
    api.EmailUserData{
      ChannelProps: api.EmailUserChannelProps{
        Recipients: []string{"[email protected]"},
      },
    },
  )

  client.V2.AlertChannels.Creates(emailAlertChan)
```

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune authored Jul 26, 2021
1 parent 03d527e commit 45cc8f4
Show file tree
Hide file tree
Showing 7 changed files with 856 additions and 0 deletions.
25 changes: 25 additions & 0 deletions api/_examples/alert-channels-v2/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"fmt"
"log"
"os"

"github.com/lacework/go-sdk/api"
)

func main() {
lacework, err := api.NewClient(os.Getenv("LW_ACCOUNT"), api.WithApiV2(),
api.WithApiKeys(os.Getenv("LW_API_KEY"), os.Getenv("LW_API_SECRET")))
if err != nil {
log.Fatal(err)
}

alertChannel, err := lacework.V2.AlertChannels.GetEmailUser("CUSTOMER_8EB5E8092016A0B8CBD8CB591362344E3A87761B997ABA0")
if err != nil {
log.Fatal(err)
}

// Output: Alert channel: THE-INTEGRATION-GUID
fmt.Printf("Alert channel: %s", alertChannel.Data.Data.ChannelProps.Recipients[0])
}
186 changes: 186 additions & 0 deletions api/alert_channels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// Copyright:: Copyright 2021, Lacework Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package api

import (
"fmt"

"github.com/pkg/errors"
)

// AlertChannelsService is the service that interacts with
// the AlertChannels schema from the Lacework APIv2 Server
type AlertChannelsService struct {
client *Client
}

// NewAlertChannel returns an instance of the AlertChannelRaw struct with the
// provided Alert Channel integration type, name and raw data as an interface{}.
//
// NOTE: This function must be used by any Alert Channel type.
//
// Basic usage: Initialize a new EmailUserAlertChannel struct, then
// use the new instance to do CRUD operations
//
// client, err := api.NewClient("account")
// if err != nil {
// return err
// }
//
// emailAlertChan := api.NewAlertChannel("foo",
// api.EmailUserAlertChannel,
// api.EmailUserData{
// ChannelProps: api.EmailUserChannelProps{
// Recipients: []string{"[email protected]"},
// },
// },
// )
//
// client.V2.AlertChannels.Creates(emailAlertChan)
//
func NewAlertChannel(name string, iType alertChannelType, data interface{}) AlertChannelRaw {
return AlertChannelRaw{
v2CommonIntegrationData: v2CommonIntegrationData{
Name: name,
Type: iType.String(),
Enabled: 1,
},
Data: data,
}
}

// AlertChannel is an interface that helps us implement a few functions
// that any Alert Channel might use, there are some cases, like during
// Update, where we need to get the ID of the Alert Channel and its type,
// this will allow users to pass any Alert Channel that implements these
// methods
type AlertChannel interface {
ID() string
AlertChannelType() alertChannelType
}

type alertChannelType int

const (
// type that defines a non-existing Alert Channel integration
NoneAlertChannel alertChannelType = iota
EmailUserAlertChannel
)

// AlertChannelTypes is the list of available Alert Channel integration types
var AlertChannelTypes = map[alertChannelType]string{
NoneAlertChannel: "None",
EmailUserAlertChannel: "EmailUser",
}

// String returns the string representation of a Alert Channel integration type
func (i alertChannelType) String() string {
return AlertChannelTypes[i]
}

// FindAlertChannelType looks up inside the list of available alert channel types
// the matching type from the provided string, if none, returns NoneAlertChannel
func FindAlertChannelType(alertChannel string) (alertChannelType, bool) {
for cType, cStr := range AlertChannelTypes {
if cStr == alertChannel {
return cType, true
}
}
return NoneAlertChannel, false
}

// List returns a list of Alert Channel integrations
func (svc *AlertChannelsService) List() (response AlertChannelsResponse, err error) {
err = svc.client.RequestDecoder("GET", apiV2AlertChannels, nil, &response)
return
}

// Create creates a single Alert Channel integration
func (svc *AlertChannelsService) Create(integration AlertChannelRaw) (
response AlertChannelResponse,
err error,
) {
err = svc.create(integration, &response)
return
}

// Delete deletes a Alert Channel integration that matches the provided guid
func (svc *AlertChannelsService) Delete(guid string) error {
if guid == "" {
return errors.New("specify an intgGuid")
}

return svc.client.RequestDecoder(
"DELETE",
fmt.Sprintf(apiV2AlertChannelFromGUID, guid),
nil,
nil,
)
}

// Get returns a raw response of the Alert Channel with the matching integration guid.
//
// To return a more specific Go struct of a Alert Channel integration, use the proper
// method such as GetEmailUser() where the function name is composed by:
//
// Get<Type>(guid)
//
// Where <Type> is the Alert Channel integration type.
func (svc *AlertChannelsService) Get(guid string) (response AlertChannelResponse, err error) {
err = svc.get(guid, &response)
return
}

type AlertChannelRaw struct {
v2CommonIntegrationData
Data interface{} `json:"data,omitempty"`
}

func (alert AlertChannelRaw) AlertChannelType() alertChannelType {
t, _ := FindAlertChannelType(alert.Type)
return t
}

type AlertChannelResponse struct {
Data AlertChannelRaw `json:"data"`
}

type AlertChannelsResponse struct {
Data []AlertChannelRaw `json:"data"`
}

func (svc *AlertChannelsService) create(data interface{}, response interface{}) error {
return svc.client.RequestEncoderDecoder("POST", apiV2AlertChannels, data, response)
}

func (svc *AlertChannelsService) get(guid string, response interface{}) error {
if guid == "" {
return errors.New("specify an intgGuid")
}
apiPath := fmt.Sprintf(apiV2AlertChannelFromGUID, guid)
return svc.client.RequestDecoder("GET", apiPath, nil, response)
}

func (svc *AlertChannelsService) update(guid string, data interface{}, response interface{}) error {
if guid == "" {
return errors.New("specify an intgGuid")
}
apiPath := fmt.Sprintf(apiV2AlertChannelFromGUID, guid)
return svc.client.RequestEncoderDecoder("PATCH", apiPath, data, response)
}
123 changes: 123 additions & 0 deletions api/alert_channels_email_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// Copyright:: Copyright 2021, Lacework Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package api

import (
"encoding/json"
"strings"
)

// GetEmailUser gets a single EmailUser alert channel matching the
// provided integration guid
func (svc *AlertChannelsService) GetEmailUser(guid string) (
response EmailUserAlertChannelResponse,
err error,
) {

// by default, expect the correct response, if not, try the workaround
err = svc.get(guid, &response)
if err == nil {
return
}

// Workaround from APIv2
// Bug: https://lacework.atlassian.net/browse/RAIN-20070
//
// This means that the response.Data.Data.ChannelProps.Recipients is a 'string'
// instead of '[]string'. We will try to deserialize and cast to correct response
var getResponse emailUserGetAlertChannelResponse
err = svc.get(guid, &getResponse)
if err != nil {
return
}

// convert GET response to a consistent response
response, err = convertGetEmailUserAlertChannelResponse(getResponse)
return
}

// UpdateEmailUser updates a single EmailUser integration on the Lacework Server
func (svc *AlertChannelsService) UpdateEmailUser(data AlertChannel) (
response EmailUserAlertChannelResponse,
err error,
) {
err = svc.update(data.ID(), data, &response)
return
}

type EmailUserAlertChannelResponse struct {
Data EmailUserIntegration `json:"data"`
}

type EmailUserIntegration struct {
v2CommonIntegrationData
Data EmailUserData `json:"data"`
}

type EmailUserData struct {
ChannelProps EmailUserChannelProps `json:"channelProps"`
NotificationTypes struct {
Properties interface{} `json:"properties,omitempty"`
} `json:"notificationTypes"`
}

type EmailUserChannelProps struct {
Recipients []string `json:"recipients"`
}

// Workaround from APIv2
// Bug: https://lacework.atlassian.net/browse/RAIN-20070
type emailUserGetData struct {
ChannelProps struct {
Recipients interface{} `json:"recipients"`
} `json:"channelProps"`
NotificationTypes struct {
Properties interface{} `json:"properties,omitempty"`
} `json:"notificationTypes"`
}
type emailUserGetIntegration struct {
v2CommonIntegrationData
Data emailUserGetData `json:"data"`
}
type emailUserGetAlertChannelResponse struct {
Data emailUserGetIntegration `json:"data"`
}

func convertGetEmailUserAlertChannelResponse(
res emailUserGetAlertChannelResponse) (EmailUserAlertChannelResponse, error) {

recipientsString, ok := res.Data.Data.ChannelProps.Recipients.(string)
if ok {
// deserialize string
res.Data.Data.ChannelProps.Recipients = strings.Split(recipientsString, ",")
}

return castEmailUserAlertChannelResponse(res)
}

func castEmailUserAlertChannelResponse(
res interface{}) (r EmailUserAlertChannelResponse, err error) {
var j []byte
j, err = json.Marshal(res)
if err != nil {
return
}
err = json.Unmarshal(j, &r)
return
}
Loading

0 comments on commit 45cc8f4

Please sign in to comment.