diff --git a/api/_examples/cloud-accounts/aws-cfg/main.go b/api/_examples/cloud-accounts/aws-cfg/main.go new file mode 100644 index 000000000..0406d4651 --- /dev/null +++ b/api/_examples/cloud-accounts/aws-cfg/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/lacework/go-sdk/api" +) + +func main() { + lacework, err := api.NewClient(os.Getenv("LW_ACCOUNT"), + api.WithSubaccount(os.Getenv("LW_SUBACCOUNT")), + api.WithApiKeys(os.Getenv("LW_API_KEY"), os.Getenv("LW_API_SECRET")), + api.WithApiV2(), + ) + if err != nil { + log.Fatal(err) + } + + res, err := lacework.V2.CloudAccounts.List() + if err != nil { + log.Fatal(err) + } + + for _, account := range res.Data { + support := "Unsupported" + switch account.Type { + case api.AwsCfgCloudAccount.String(): + support = "Supported" + } + + // Output: INTEGRATION-GUID:INTEGRATION-TYPE:[Supported|Unsupported] + fmt.Printf("%s:%s:%s\n", account.IntgGuid, account.Type, support) + } + + awsCfgData := api.AwsCfgData{ + Credentials: api.AwsCfgCredentials{ + RoleArn: "arn:aws:iam::1234567890:role/lacework_iam_example_role", + ExternalID: "abc123", + }, + } + + awsCtSqsCloudAccount := api.NewCloudAccount( + "aws-cfg-from-golang", + api.AwsCfgCloudAccount, + awsCfgData, + ) + + awsCfgResponse, err := lacework.V2.CloudAccounts.Create(awsCtSqsCloudAccount) + if err != nil { + log.Fatal(err) + } + + // Output: AwsCfg Cloud Account created: THE-INTEGRATION-GUID + fmt.Printf("Cloud Account created: %s", awsCfgResponse.Data.IntgGuid) +} diff --git a/api/_examples/cloud-accounts/aws-eks-ct-sqs/main.go b/api/_examples/cloud-accounts/aws-ct-sqs/main.go similarity index 100% rename from api/_examples/cloud-accounts/aws-eks-ct-sqs/main.go rename to api/_examples/cloud-accounts/aws-ct-sqs/main.go diff --git a/api/cloud_accounts_aws_cfg.go b/api/cloud_accounts_aws_cfg.go new file mode 100644 index 000000000..e765e3bd8 --- /dev/null +++ b/api/cloud_accounts_aws_cfg.go @@ -0,0 +1,57 @@ +// +// Author:: Darren Murray () +// Copyright:: Copyright 2022, 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 + +// GetAwsCfg gets a single AwsCfg integration matching the +// provided integration guid +func (svc *CloudAccountsService) GetAwsCfg(guid string) ( + response AwsCfgIntegrationResponse, + err error, +) { + err = svc.get(guid, &response) + return +} + +// UpdateAwsCfg updates a single AwsCfg integration on the Lacework Server +func (svc *CloudAccountsService) UpdateAwsCfg(data CloudAccount) ( + response AwsCfgIntegrationResponse, + err error, +) { + err = svc.update(data.ID(), data, &response) + return +} + +type AwsCfgIntegrationResponse struct { + Data AwsCfg `json:"data"` +} + +type AwsCfg struct { + v2CommonIntegrationData + Data AwsCfgData `json:"data"` +} + +type AwsCfgData struct { + Credentials AwsCfgCredentials `json:"crossAccountCredentials"` + AwsAccountID string `json:"awsAccountId,omitempty"` +} + +type AwsCfgCredentials struct { + RoleArn string `json:"roleArn"` + ExternalID string `json:"externalId"` +} diff --git a/api/cloud_accounts_aws_cfg_test.go b/api/cloud_accounts_aws_cfg_test.go new file mode 100644 index 000000000..9285f8161 --- /dev/null +++ b/api/cloud_accounts_aws_cfg_test.go @@ -0,0 +1,163 @@ +// +// Author:: Darren Murray () +// Copyright:: Copyright 2022, 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_test + +import ( + "fmt" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/lacework/go-sdk/api" + "github.com/lacework/go-sdk/internal/intgguid" + "github.com/lacework/go-sdk/internal/lacework" +) + +func TestCloudAccountsNewAwsCfgWithCustomTemplateFile(t *testing.T) { + awsCfgData := api.AwsCfgData{ + Credentials: api.AwsCfgCredentials{ + RoleArn: "arn:foo:bar", + ExternalID: "0123456789", + }, + } + + subject := api.NewCloudAccount("integration_name", api.AwsCfgCloudAccount, awsCfgData) + assert.Equal(t, api.AwsCfgCloudAccount.String(), subject.Type) + + // casting the data interface{} to type AwsCfgData + subjectData := subject.Data.(api.AwsCfgData) + + assert.Equal(t, subjectData.Credentials.RoleArn, "arn:foo:bar") + assert.Equal(t, subjectData.Credentials.ExternalID, "0123456789") +} + +func TestCloudAccountsAwsCfgGet(t *testing.T) { + var ( + intgGUID = intgguid.New() + apiPath = fmt.Sprintf("CloudAccounts/%s", intgGUID) + fakeServer = lacework.MockServer() + ) + fakeServer.UseApiV2() + fakeServer.MockToken("TOKEN") + defer fakeServer.Close() + + fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "GET", r.Method, "GetAwsCfg() should be a GET method") + fmt.Fprintf(w, generateCloudAccountResponse(singleAwsCfgCloudAccount(intgGUID))) + }) + + c, err := api.NewClient("test", + api.WithApiV2(), + api.WithToken("TOKEN"), + api.WithURL(fakeServer.URL()), + ) + assert.Nil(t, err) + + response, err := c.V2.CloudAccounts.GetAwsCfg(intgGUID) + assert.Nil(t, err) + assert.NotNil(t, response) + assert.Equal(t, intgGUID, response.Data.IntgGuid) + assert.Equal(t, "integration_name", response.Data.Name) + assert.True(t, response.Data.State.Ok) + assert.Equal(t, "arn:foo:bar", response.Data.Data.Credentials.RoleArn) + assert.Equal(t, "0123456789", response.Data.Data.Credentials.ExternalID) +} + +func TestCloudAccountsAwsCfgUpdate(t *testing.T) { + var ( + intgGUID = intgguid.New() + apiPath = fmt.Sprintf("CloudAccounts/%s", intgGUID) + fakeServer = lacework.MockServer() + ) + fakeServer.UseApiV2() + fakeServer.MockToken("TOKEN") + defer fakeServer.Close() + + fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "PATCH", r.Method, "UpdateAwsCfg() should be a PATCH method") + + if assert.NotNil(t, r.Body) { + body := httpBodySniffer(r) + assert.Contains(t, body, intgGUID, "INTG_GUID missing") + assert.Contains(t, body, "integration_name", "cloud account name is missing") + assert.Contains(t, body, "AwsCfg", "wrong cloud account type") + assert.Contains(t, body, "arn:foo:bar", "wrong role arn") + assert.Contains(t, body, "0123456789", "wrong external ID") + assert.Contains(t, body, "enabled\":1", "cloud account is not enabled") + } + + fmt.Fprintf(w, generateCloudAccountResponse(singleAwsCfgCloudAccount(intgGUID))) + }) + + c, err := api.NewClient("test", + api.WithApiV2(), + api.WithToken("TOKEN"), + api.WithURL(fakeServer.URL()), + ) + assert.Nil(t, err) + + cloudAccount := api.NewCloudAccount("integration_name", + api.AwsCfgCloudAccount, + api.AwsCfgData{ + Credentials: api.AwsCfgCredentials{ + RoleArn: "arn:foo:bar", + ExternalID: "0123456789", + }, + }, + ) + assert.Equal(t, "integration_name", cloudAccount.Name, "AwsCfg cloud account name mismatch") + assert.Equal(t, "AwsCfg", cloudAccount.Type, "a new AwsCfg cloud account should match its type") + assert.Equal(t, 1, cloudAccount.Enabled, "a new AwsCfg cloud account should be enabled") + cloudAccount.IntgGuid = intgGUID + + response, err := c.V2.CloudAccounts.UpdateAwsCfg(cloudAccount) + assert.Nil(t, err) + assert.NotNil(t, response) + assert.Equal(t, intgGUID, response.Data.IntgGuid) + assert.Equal(t, "arn:foo:bar", response.Data.Data.Credentials.RoleArn) + assert.Equal(t, "0123456789", response.Data.Data.Credentials.ExternalID) +} + +func singleAwsCfgCloudAccount(id string) string { + return ` + { + "createdOrUpdatedBy": "salim.afiunemaya@lacework.net", + "createdOrUpdatedTime": "2021-06-01T19:28:00.092Z", + "data": { + "awsAccountId": "123456789000", + "crossAccountCredentials": { + "externalId": "0123456789", + "roleArn": "arn:foo:bar" + } + }, + "enabled": 1, + "intgGuid": "` + id + `", + "isOrg": 0, + "name": "integration_name", + "state": { + "details": {}, + "lastSuccessfulTime": 1624456896915, + "lastUpdatedTime": 1624456896915, + "ok": true + }, + "type": "AwsCfg" + } + ` +} diff --git a/api/cloud_accounts_test.go b/api/cloud_accounts_test.go index e3e085543..723cda75a 100644 --- a/api/cloud_accounts_test.go +++ b/api/cloud_accounts_test.go @@ -183,12 +183,13 @@ func TestCloudAccountsDelete(t *testing.T) { func TestCloudAccountsList(t *testing.T) { var ( awsIntgGUIDs = []string{intgguid.New(), intgguid.New(), intgguid.New()} + awsCfgGUIDs = []string{intgguid.New(), intgguid.New(), intgguid.New()} awsEksAuditLogGUIDs = []string{intgguid.New()} azureIntgGUIDs = []string{intgguid.New(), intgguid.New()} gcpIntgGUIDs = []string{ intgguid.New(), intgguid.New(), intgguid.New(), intgguid.New(), } - allGUIDs = append(awsEksAuditLogGUIDs, append(azureIntgGUIDs, append(gcpIntgGUIDs, awsIntgGUIDs...)...)...) + allGUIDs = append(awsEksAuditLogGUIDs, append(azureIntgGUIDs, append(awsCfgGUIDs, append(gcpIntgGUIDs, awsIntgGUIDs...)...)...)...) expectedLen = len(allGUIDs) fakeServer = lacework.MockServer() ) @@ -201,6 +202,7 @@ func TestCloudAccountsList(t *testing.T) { generateCloudAccounts(awsIntgGUIDs, "AwsCtSqs"), generateCloudAccounts(awsEksAuditLogGUIDs, "AwsEksAudit"), // TODO @afiune come back here and update these Cloud Accounts types when they exist + generateCloudAccounts(awsCfgGUIDs, "AwsCfg"), generateCloudAccounts(gcpIntgGUIDs, "AwsCtSqs"), // "GcpCfg"), generateCloudAccounts(azureIntgGUIDs, "AwsCtSqs"), // "AzureAlSeq"), } @@ -277,10 +279,8 @@ func generateCloudAccounts(guids []string, iType string) string { cloudAccounts[i] = singleAwsCtSqsCloudAccount(guid) case api.AwsEksAuditCloudAccount.String(): cloudAccounts[i] = singleAwsEksAuditCloudAccount(guid) - // TODO @afiune come back here and update these Cloud Accounts types - // when they exist - //case api.AwsCfgCloudAccount.String(): - //cloudAccounts[i] = singleAwsCfgCloudAccount(guid) + case api.AwsCfgCloudAccount.String(): + cloudAccounts[i] = singleAwsCfgCloudAccount(guid) } } return strings.Join(cloudAccounts, ", ")