diff --git a/api/_examples/cloud-accounts/azure-ad-al/main.go b/api/_examples/cloud-accounts/azure-ad-al/main.go new file mode 100644 index 000000000..0a3fee634 --- /dev/null +++ b/api/_examples/cloud-accounts/azure-ad-al/main.go @@ -0,0 +1,59 @@ +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")), + ) + 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.AzureAdAlCloudAccount.String(): + support = "Supported" + } + + // Output: INTEGRATION-GUID:INTEGRATION-TYPE:[Supported|Unsupported] + fmt.Printf("%s:%s:%s\n", account.IntgGuid, account.Type, support) + } + + azureAdAlData := api.AzureAdAlData{ + Credentials: api.AzureAdAlCredentials{ + ClientID: "client-id", + ClientSecret: "some-secret", + }, + TenantID: "tenant-id", + EventHubNamespace: "EventHubNamespace", + EventHubName: "EventHubName", + } + + azureAdAlCloudAccount := api.NewCloudAccount( + "cloud-from-golang", + api.AzureAdAlCloudAccount, + azureAdAlData, + ) + + azureAdAlIntegrationResponse, err := lacework.V2.CloudAccounts.Create(azureAdAlCloudAccount) + if err != nil { + log.Fatal(err) + } + + // Output: AzureAdAl Cloud Account created: THE-INTEGRATION-GUID + fmt.Printf("Cloud Account created: %s", azureAdAlIntegrationResponse.Data.IntgGuid) +} diff --git a/api/cloud_accounts.go b/api/cloud_accounts.go index ebb54282a..4ad222120 100644 --- a/api/cloud_accounts.go +++ b/api/cloud_accounts.go @@ -91,6 +91,7 @@ const ( AwsSidekickOrgCloudAccount AwsUsGovCfgCloudAccount AwsUsGovCtSqsCloudAccount + AzureAdAlCloudAccount AzureAlSeqCloudAccount AzureCfgCloudAccount GcpAtSesCloudAccount @@ -112,6 +113,7 @@ var CloudAccountTypes = map[cloudAccountType]string{ AwsSidekickOrgCloudAccount: "AwsSidekickOrg", AwsUsGovCfgCloudAccount: "AwsUsGovCfg", AwsUsGovCtSqsCloudAccount: "AwsUsGovCtSqs", + AzureAdAlCloudAccount: "AzureAdAl", AzureAlSeqCloudAccount: "AzureAlSeq", AzureCfgCloudAccount: "AzureCfg", GcpAtSesCloudAccount: "GcpAtSes", diff --git a/api/cloud_accounts_azure_ad_al.go b/api/cloud_accounts_azure_ad_al.go new file mode 100644 index 000000000..be008791c --- /dev/null +++ b/api/cloud_accounts_azure_ad_al.go @@ -0,0 +1,59 @@ +// +// Author:: Rubinder Singh () +// Copyright:: Copyright 2024, 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 + +// GetAzureAdAl gets a single AzureAdAl integration matching the +// provided integration guid +func (svc *CloudAccountsService) GetAzureAdAl(guid string) ( + response AzureAdAlIntegrationResponse, + err error, +) { + err = svc.get(guid, &response) + return +} + +// UpdateAzureAdAl updates a single AzureAdAl integration on the Lacework Server +func (svc *CloudAccountsService) UpdateAzureAdAl(data CloudAccount) ( + response AzureAdAlIntegrationResponse, + err error, +) { + err = svc.update(data.ID(), data, &response) + return +} + +type AzureAdAlIntegrationResponse struct { + Data AzureAdAl `json:"data"` +} + +type AzureAdAl struct { + v2CommonIntegrationData + Data AzureAdAlData `json:"data"` +} + +type AzureAdAlData struct { + Credentials AzureAdAlCredentials `json:"credentials"` + TenantID string `json:"tenantId"` + EventHubNamespace string `json:"eventHubNamespace"` + EventHubName string `json:"eventHubName"` +} + +type AzureAdAlCredentials struct { + ClientID string `json:"clientId"` + ClientSecret string `json:"clientSecret"` +} diff --git a/api/cloud_accounts_azure_ad_al_test.go b/api/cloud_accounts_azure_ad_al_test.go new file mode 100644 index 000000000..c10fb63b1 --- /dev/null +++ b/api/cloud_accounts_azure_ad_al_test.go @@ -0,0 +1,166 @@ +// +// Author:: Rubinder Singh () +// Copyright:: Copyright 2024, 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/lacework/go-sdk/api" + "github.com/lacework/go-sdk/internal/intgguid" + "github.com/lacework/go-sdk/internal/lacework" + "github.com/stretchr/testify/assert" +) + +func TestCloudAccountsAzureAdAlGet(t *testing.T) { + var ( + intgGUID = intgguid.New() + apiPath = fmt.Sprintf("CloudAccounts/%s", intgGUID) + fakeServer = lacework.MockServer() + ) + fakeServer.MockToken("TOKEN") + defer fakeServer.Close() + + fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "GET", r.Method, "GetAzureAdAl() should be a GET method") + fmt.Fprintf(w, generateCloudAccountResponse(azureAdAlCloudAccount(intgGUID))) + }) + + c, err := api.NewClient("test", + api.WithToken("TOKEN"), + api.WithURL(fakeServer.URL()), + ) + assert.Nil(t, err) + + response, err := c.V2.CloudAccounts.GetAzureAdAl(intgGUID) + assert.Nil(t, err) + assert.NotNil(t, response) + assert.Equal(t, intgGUID, response.Data.IntgGuid) + assert.Equal(t, "azure_ad_al_integration_test", response.Data.Name) + assert.True(t, response.Data.State.Ok) + assert.Equal(t, "123456777", response.Data.Data.Credentials.ClientID) + assert.Equal(t, "test-secret-1234", response.Data.Data.Credentials.ClientSecret) + assert.Equal(t, "AzureAdAl", response.Data.Type) + assert.Equal(t, "tenant-1", response.Data.Data.TenantID) + assert.Equal(t, "eventHubNamespace-1", response.Data.Data.EventHubNamespace) + assert.Equal(t, "eventHubName-1", response.Data.Data.EventHubName) +} + +func TestCloudAccountsAzureAdAlUpdate(t *testing.T) { + var ( + intgGUID = intgguid.New() + apiPath = fmt.Sprintf("CloudAccounts/%s", intgGUID) + fakeServer = lacework.MockServer() + intgData = api.AzureAdAlData{ + TenantID: "tenant-1", + Credentials: api.AzureAdAlCredentials{ + ClientID: "123456777", + ClientSecret: "test-secret-1234", + }, + EventHubNamespace: "eventHubNamespace-1", + EventHubName: "eventHubName-1", + } + ) + fakeServer.MockToken("TOKEN") + defer fakeServer.Close() + + // Step 1 - Start Fake Server to return updated data + fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "PATCH", r.Method, "UpdateAzureAdAl() 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, "azure_ad_al_integration_test", "cloud account name is missing") + assert.Contains(t, body, "AzureAdAl", "wrong cloud account type") + assert.Contains(t, body, intgData.Credentials.ClientID, "wrong ClientId") + assert.Contains(t, body, intgData.Credentials.ClientSecret, "wrong ClientSecret") + assert.Contains(t, body, intgData.TenantID, "wrong TenantId") + assert.Contains(t, body, intgData.EventHubNamespace, "wrong EventHubNamespace") + assert.Contains(t, body, intgData.EventHubName, "wrong EventHubName") + assert.Contains(t, body, "enabled\":1", "cloud account is not enabled") + } + + fmt.Fprintf(w, generateCloudAccountResponse(azureAdAlCloudAccount(intgGUID))) + }) + + c, err := api.NewClient("test", + api.WithToken("TOKEN"), + api.WithURL(fakeServer.URL()), + ) + assert.Nil(t, err) + + // Step 2 - Get Updated data from Fake server + cloudAccount := api.NewCloudAccount("azure_ad_al_integration_test", + api.AzureAdAlCloudAccount, + intgData, + ) + + cloudAccount.IntgGuid = intgGUID + response, err := c.V2.CloudAccounts.UpdateAzureAdAl(cloudAccount) + assert.Nil(t, err, "Cannot update integration") + assert.NotNil(t, response) + integration := response.Data + assert.Equal(t, intgGUID, integration.IntgGuid) + + integrationData := integration.Data + assert.Equal(t, "azure_ad_al_integration_test", cloudAccount.Name) + assert.Equal(t, "AzureAdAl", cloudAccount.Type) + assert.Equal(t, 1, cloudAccount.Enabled) + assert.Equal(t, "tenant-1", integrationData.TenantID) + assert.Equal(t, "eventHubNamespace-1", integrationData.EventHubNamespace) + assert.Equal(t, "eventHubName-1", integrationData.EventHubName) + assert.Equal(t, "123456777", integrationData.Credentials.ClientID) + assert.Equal(t, "test-secret-1234", integrationData.Credentials.ClientSecret) +} + +func azureAdAlCloudAccount(id string) string { + return fmt.Sprintf(`{ + "createdOrUpdatedBy": "rubinder.singh@lacework.net", + "createdOrUpdatedTime": "2024-03-11T00:00:00.000Z", + "enabled": 1, + "intgGuid": %q, + "isOrg": 0, + "name": "azure_ad_al_integration_test", + "state": { + "ok": true, + "lastUpdatedTime": 1710104691000, + "lastSuccessfulTime": 1710104691000, + "details": { + "queueRx": "OK", + "decodeNtfn": "OK", + "logFileGet": "OK", + "queueDel": "OK", + "lastMsgRxTime": 1710104691000, + "noData": true + } + }, + "type": "AzureAdAl", + "data": { + "credentials": { + "clientId": "123456777", + "clientSecret": "test-secret-1234" + }, + "tenantId": "tenant-1", + "eventHubNamespace": "eventHubNamespace-1", + "eventHubName": "eventHubName-1" + } + }`, id) +}