From 971cf13f0765551fa50e981f7fcaeac93aff7a86 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Tue, 29 Oct 2024 17:10:29 -0500 Subject: [PATCH 01/17] feat: Remove RGv1 logic --- .../resource_groups/kubernetes.json | 0 api/feature_flags.go | 2 - api/resource_groups.go | 99 +++-- api/resource_groups_aws.go | 171 -------- api/resource_groups_aws_test.go | 158 -------- api/resource_groups_azure.go | 167 -------- api/resource_groups_azure_test.go | 143 ------- api/resource_groups_container.go | 167 -------- api/resource_groups_container_test.go | 143 ------- api/resource_groups_gcp.go | 165 -------- api/resource_groups_gcp_test.go | 143 ------- api/resource_groups_lw_account.go | 161 -------- api/resource_groups_lw_account_test.go | 140 ------- api/resource_groups_machine.go | 163 -------- api/resource_groups_machine_test.go | 140 ------- api/resource_groups_test.go | 350 +++++++++++++++- api/resource_groups_v2.go | 3 - api/resource_groups_version_service.go | 373 ------------------ api/v2.go | 4 +- cli/cmd/resource_group_aws.go | 95 ----- cli/cmd/resource_group_azure.go | 106 ----- cli/cmd/resource_group_container.go | 114 ------ cli/cmd/resource_group_gcp.go | 105 ----- cli/cmd/resource_group_lw_account.go | 103 ----- cli/cmd/resource_group_machine.go | 102 ----- cli/cmd/resource_group_v2.go | 3 +- cli/cmd/resource_groups.go | 253 ++++-------- 27 files changed, 468 insertions(+), 3105 deletions(-) create mode 100644 api/_templates/resource_groups/kubernetes.json delete mode 100644 api/resource_groups_aws.go delete mode 100644 api/resource_groups_aws_test.go delete mode 100644 api/resource_groups_azure.go delete mode 100644 api/resource_groups_azure_test.go delete mode 100644 api/resource_groups_container.go delete mode 100644 api/resource_groups_container_test.go delete mode 100644 api/resource_groups_gcp.go delete mode 100644 api/resource_groups_gcp_test.go delete mode 100644 api/resource_groups_lw_account.go delete mode 100644 api/resource_groups_lw_account_test.go delete mode 100644 api/resource_groups_machine.go delete mode 100644 api/resource_groups_machine_test.go delete mode 100644 api/resource_groups_version_service.go delete mode 100644 cli/cmd/resource_group_aws.go delete mode 100644 cli/cmd/resource_group_azure.go delete mode 100644 cli/cmd/resource_group_container.go delete mode 100644 cli/cmd/resource_group_gcp.go delete mode 100644 cli/cmd/resource_group_lw_account.go delete mode 100644 cli/cmd/resource_group_machine.go diff --git a/api/_templates/resource_groups/kubernetes.json b/api/_templates/resource_groups/kubernetes.json new file mode 100644 index 000000000..e69de29bb diff --git a/api/feature_flags.go b/api/feature_flags.go index 22f341381..8fa897c6e 100644 --- a/api/feature_flags.go +++ b/api/feature_flags.go @@ -4,8 +4,6 @@ import ( "fmt" ) -const ApiV2CliFeatureFlag = "PUBLIC.rgv2.cli" - type FeatureFlagsService struct { client *Client } diff --git a/api/resource_groups.go b/api/resource_groups.go index 170843236..9f0561a5a 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -22,7 +22,6 @@ import ( _ "embed" "encoding/json" "fmt" - "strconv" "time" "github.com/lacework/go-sdk/lwtime" @@ -63,11 +62,8 @@ const ( ContainerResourceGroup GcpResourceGroup MachineResourceGroup - - // requires Org Access account client.WithOrgAccess() - LwAccountResourceGroup - OciResourceGroup + KubernetesResourceGroup ) // query templates @@ -86,6 +82,8 @@ var ( LwAccountResourceGroupQueryTemplate string = "" //go:embed _templates/resource_groups/oci.json OciResourceGroupQueryTemplate string + //go:embed _templates/resource_groups/kubernetes.json + KubernetesResourceGroupQueryTemplate string ) type resourceGroupContext struct { @@ -100,9 +98,20 @@ var ResourceGroupTypes = map[resourceGroupType]resourceGroupContext{ AzureResourceGroup: {resourceGroupType: "AZURE", queryTemplate: AzureResourceGroupQueryTemplate}, ContainerResourceGroup: {resourceGroupType: "CONTAINER", queryTemplate: ContainerResourceGroupQueryTemplate}, GcpResourceGroup: {resourceGroupType: "GCP", queryTemplate: GcpResourceGroupQueryTemplate}, - LwAccountResourceGroup: {resourceGroupType: "LW_ACCOUNT", queryTemplate: LwAccountResourceGroupQueryTemplate}, MachineResourceGroup: {resourceGroupType: "MACHINE", queryTemplate: MachineResourceGroupQueryTemplate}, OciResourceGroup: {resourceGroupType: "OCI", queryTemplate: OciResourceGroupQueryTemplate}, + KubernetesResourceGroup: {resourceGroupType: "KUBERNETES", queryTemplate: KubernetesResourceGroupQueryTemplate}, +} + +func NewResourceGroup(name string, iType resourceGroupType, + description string, query *RGQuery) ResourceGroupDataWithQuery { + return ResourceGroupDataWithQuery{ + Name: name, + Type: iType.String(), + Enabled: 1, + Query: query, + Description: description, + } } // String returns the string representation of a Resource Group type @@ -171,19 +180,14 @@ func (svc *ResourceGroupsService) Update(data ResourceGroup) ( } func castResourceGroupResponse(data resourceGroupWorkaroundData, response interface{}) error { - isDefault, err := strconv.Atoi(data.IsDefault) - if err != nil { - return err - } group := ResourceGroupResponse{ Data: ResourceGroupData{ - Guid: data.Guid, - IsDefault: isDefault, - ResourceGuid: data.ResourceGuid, + IsDefaultBoolean: data.IsDefaultBoolean, + ResourceGroupGuid: data.ResourceGroupGuid, Name: data.Name, Type: data.Type, Enabled: data.Enabled, - Props: data.Props, + Query: data.Query, }, } @@ -212,6 +216,25 @@ func setResourceGroupsResponse(workaround resourceGroupsWorkaroundResponse) (Res return ResourceGroupsResponse{Data: data}, nil } +func setResourceGroupResponse(response resourceGroupWorkaroundData) (ResourceGroupResponse, + error) { + return ResourceGroupResponse{ + Data: ResourceGroupData{ + Type: response.Type, + Enabled: response.Enabled, + Name: response.Name, + Query: response.Query, + Description: response.Description, + ResourceGroupGuid: response.ResourceGroupGuid, + CreatedTime: response.CreatedTime, + CreatedBy: response.CreatedBy, + UpdatedTime: response.UpdatedTime, + UpdatedBy: response.UpdatedBy, + IsDefaultBoolean: response.IsDefaultBoolean, + }, + }, nil +} + // Delete deletes a Resource Group that matches the provided resource guid func (svc *ResourceGroupsService) Delete(guid string) error { if guid == "" { @@ -271,26 +294,16 @@ func (group ResourceGroupData) ResourceGroupType() resourceGroupType { } func (group ResourceGroupData) ID() string { - if !group.IsV2Group() { - return group.ResourceGuid - } else { - return group.ResourceGroupGuid - } + return group.ResourceGroupGuid } -func (group *ResourceGroupData) ResetRGV2Fields() { +func (group *ResourceGroupData) ResetResourceGUID() { + group.ResourceGroupGuid = "" group.UpdatedBy = "" group.UpdatedTime = nil group.CreatedBy = "" group.CreatedTime = nil group.IsDefaultBoolean = nil - group.IsOrg = nil -} - -func (group *ResourceGroupData) ResetResourceGUID() { - group.ResourceGuid = "" - group.ResourceGroupGuid = "" - group.ResetRGV2Fields() } func (group ResourceGroupData) Status() string { @@ -300,10 +313,6 @@ func (group ResourceGroupData) Status() string { return "Disabled" } -func (group ResourceGroupData) IsV2Group() bool { - return group.Query != nil -} - type ResourceGroupResponse struct { Data ResourceGroupData `json:"data"` } @@ -313,17 +322,7 @@ type ResourceGroupsResponse struct { } type ResourceGroupData struct { - // RGv1 Fields - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName,omitempty"` - Type string `json:"resourceType"` - Enabled int `json:"enabled"` - Props interface{} `json:"props,omitempty"` - - // RG v2 Fields. `Enabled` and `Type` fields are the same in RGv1 nd RGv2 - NameV2 string `json:"name,omitempty"` + Name string `json:"name,omitempty"` Query *RGQuery `json:"query,omitempty"` Description string `json:"description,omitempty"` ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` @@ -332,7 +331,8 @@ type ResourceGroupData struct { UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - IsOrg *bool `json:"isOrg,omitempty"` + Type string `json:"resourceType"` + Enabled int `json:"enabled"` } // RAIN-21510 workaround @@ -345,15 +345,7 @@ type resourceGroupsWorkaroundResponse struct { } type resourceGroupWorkaroundData struct { - Guid string `json:"guid,omitempty"` - IsDefault string `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props interface{} `json:"props"` - - NameV2 string `json:"name,omitempty"` + Name string `json:"name,omitempty"` Query *RGQuery `json:"query,omitempty"` Description string `json:"description,omitempty"` ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` @@ -362,5 +354,6 @@ type resourceGroupWorkaroundData struct { UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - IsOrg *bool `json:"isOrg,omitempty"` + Type string `json:"resourceType"` + Enabled int `json:"enabled,omitempty"` } diff --git a/api/resource_groups_aws.go b/api/resource_groups_aws.go deleted file mode 100644 index 96efcfe01..000000000 --- a/api/resource_groups_aws.go +++ /dev/null @@ -1,171 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all aws accounts -var ( - AwsResourceGroupAllAccounts = []string{"*"} -) - -// GetAws gets a single Aws ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetAws(guid string) ( - response AwsResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setAwsResourceGroupResponse(rawResponse) -} - -// UpdateAws updates a single Aws ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateAws(data ResourceGroup) ( - response AwsResourceGroupResponse, err error) { - - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - if err != nil { - return - } - - return -} - -// CreateAws creates a single Aws ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateAws(data ResourceGroup) ( - response AwsResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - return -} - -func setAwsResourceGroupResponse(response resourceGroupWorkaroundResponse) (aws AwsResourceGroupResponse, err error) { - var props AwsResourceJsonStringGroupProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - aws = AwsResourceGroupResponse{ - Data: AwsResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - aws.Data.Props = AwsResourceGroupProps(props) - return -} - -type AwsResourceGroupResponse struct { - Data AwsResourceGroupData `json:"data"` -} - -type AwsResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props AwsResourceGroupProps `json:"props"` - - NameV2 string `json:"name"` - Query *RGQuery `json:"query"` - Description string `json:"description,omitempty"` - ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` - CreatedTime *lwtime.Epoch `json:"lastUpdated,omitempty"` - CreatedBy string `json:"createdBy,omitempty"` - UpdatedTime *lwtime.Epoch `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` - IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - IsOrg *bool `json:"isOrg,omitempty"` -} - -type AwsResourceGroupProps struct { - Description string `json:"description,omitempty"` - AccountIDs []string `json:"accountIds"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type AwsResourceJsonStringGroupProps struct { - Description string `json:"DESCRIPTION,omitempty"` - AccountIDs []string `json:"ACCOUNT_IDS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props AwsResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props AwsResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - AccountIDs []string `json:"accountIds"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - AccountIDs: props.AccountIDs, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_aws_test.go b/api/resource_groups_aws_test.go deleted file mode 100644 index fb2ebf4dc..000000000 --- a/api/resource_groups_aws_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// -// Author:: Darren Murray () -// 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_test - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "testing" - "time" - - "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 TestResourceGroupAwsGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetAwsResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleAwsResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetAws(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Aws Accounts", response.Data.Props.Description) - assert.Equal(t, []string{"*"}, response.Data.Props.AccountIDs) -} - -func TestResourceGroupsAwsUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateAwsResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "AWS", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[\"abc123\",\"cba321\"]", "wrong account ids") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleAwsResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.AwsResourceGroup, - api.AwsResourceGroupProps{ - Description: "Updated", - AccountIDs: []string{"abc123", "cba321"}, - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "Aws Resource Group name mismatch") - assert.Equal(t, "AWS", resourceGroup.Type, "a new Aws Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new Aws Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateAws(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func TestMarshallResourceGroupLastUpdatedTime(t *testing.T) { - var res api.AwsResourceGroupResponse - err := json.Unmarshal([]byte(generateResourceGroupResponse(singleAwsResourceGroupUpdateResponse("test"))), &res) - if err != nil { - log.Fatal("Unable to unmarshall aws resource group string") - } - jsonString, err := json.Marshal(res) - if err != nil { - log.Fatal("Unable to marshall aws resource group string") - } - - assert.Equal(t, res.Data.Props.LastUpdated.ToTime().UnixNano()/int64(time.Millisecond), int64(1586453993470)) - assert.Contains(t, string(jsonString), "2020-04-09T17:39:53Z") -} - -func singleAwsResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Aws Accounts\",\"ACCOUNT_IDS\":[\"*\"],\"UPDATED_BY\":null,\"LAST_UPDATED\":1586453993470}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} - -func singleAwsResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Aws Accounts", - "accountIds":["*"], - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_azure.go b/api/resource_groups_azure.go deleted file mode 100644 index b2c293f67..000000000 --- a/api/resource_groups_azure.go +++ /dev/null @@ -1,167 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all azure subscriptions -var ( - AzureResourceGroupAllSubscriptions = []string{"*"} -) - -// GetAzure gets a single Azure ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetAzure(guid string) ( - response AzureResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setAzureResponse(rawResponse) -} - -// UpdateAzure updates a single Azure ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateAzure(data ResourceGroup) ( - response AzureResourceGroupResponse, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - if err != nil { - return - } - - return -} - -// CreateAzure creates a single Azure ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateAzure(data ResourceGroup) ( - response AzureResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - return -} - -func setAzureResponse(response resourceGroupWorkaroundResponse) (az AzureResourceGroupResponse, err error) { - var props AzureResourceJsonStringGroupProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - az = AzureResourceGroupResponse{ - Data: AzureResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - - az.Data.Props = AzureResourceGroupProps(props) - - return -} - -type AzureResourceGroupResponse struct { - Data AzureResourceGroupData `json:"data"` -} - -type AzureResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props AzureResourceGroupProps `json:"props"` -} - -type AzureResourceGroupProps struct { - Description string `json:"description,omitempty"` - Tenant string `json:"tenant"` - Subscriptions []string `json:"subscriptions"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type AzureResourceJsonStringGroupProps struct { - Description string `json:"DESCRIPTION,omitempty"` - Tenant string `json:"TENANT"` - Subscriptions []string `json:"SUBSCRIPTIONS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props AzureResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props AzureResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - Tenant string `json:"tenant"` - Subscriptions []string `json:"subscriptions"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - Tenant: props.Tenant, - Subscriptions: props.Subscriptions, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_azure_test.go b/api/resource_groups_azure_test.go deleted file mode 100644 index 6e620b955..000000000 --- a/api/resource_groups_azure_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// -// Author:: Darren Murray () -// 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_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 TestResourceGroupAzureGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetAzureResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleAzureResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetAzure(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Tenants and Subscriptions", response.Data.Props.Description) - assert.Equal(t, []string{"*"}, response.Data.Props.Subscriptions) - assert.Equal(t, "*", response.Data.Props.Tenant) -} - -func TestResourceGroupsAzureUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateAzureResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "AZURE", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[\"abc123\",\"cba321\"]", "wrong subscriptions") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleAzureResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.AzureResourceGroup, - api.AzureResourceGroupProps{ - Description: "Updated", - Subscriptions: []string{"abc123", "cba321"}, - Tenant: "tenant", - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "Azure Resource Group name mismatch") - assert.Equal(t, "AZURE", resourceGroup.Type, "a new Azure Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new Azure Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateAzure(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func singleAzureResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Tenants and Subscriptions\",\"SUBSCRIPTIONS\":[\"*\"],\"TENANT\":\"*\",\"UPDATED_BY\":null,\"LAST_UPDATED\":1586453993500}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AZURE", - "enabled": 1 - } - ` -} - -func singleAzureResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Tenants and Subscriptions", - "subscriptions":["*"], - "tenant":"*", - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_container.go b/api/resource_groups_container.go deleted file mode 100644 index 197699300..000000000 --- a/api/resource_groups_container.go +++ /dev/null @@ -1,167 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all labels/tags -var ( - ContainerResourceGroupAllLabels = []map[string]string{{"*": "*"}} - ContainerResourceGroupAllTags = []string{"*"} -) - -// GetContainer gets a single Container ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetContainer(guid string) ( - response ContainerResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setContainerResponse(rawResponse) -} - -// UpdateContainer updates a single Container ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateContainer(data ResourceGroup) ( - response ContainerResourceGroupResponse, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - return -} - -// CreateContainer creates a single Container ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateContainer(data ResourceGroup) ( - response ContainerResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - if err != nil { - return - } - - return -} - -func setContainerResponse(response resourceGroupWorkaroundResponse) (ctr ContainerResourceGroupResponse, err error) { - var props ContainerResourceJsonStringGroupProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - ctr = ContainerResourceGroupResponse{ - Data: ContainerResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - - ctr.Data.Props = ContainerResourceGroupProps(props) - return -} - -type ContainerResourceGroupResponse struct { - Data ContainerResourceGroupData `json:"data"` -} - -type ContainerResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props ContainerResourceGroupProps `json:"props"` -} - -type ContainerResourceGroupProps struct { - Description string `json:"description,omitempty"` - ContainerLabels []map[string]string `json:"containerLabels"` - ContainerTags []string `json:"containerTags"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type ContainerResourceJsonStringGroupProps struct { - Description string `json:"DESCRIPTION,omitempty"` - ContainerLabels []map[string]string `json:"CONTAINER_LABELS"` - ContainerTags []string `json:"CONTAINER_TAGS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props ContainerResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props ContainerResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - ContainerLabels []map[string]string `json:"containerLabels"` - ContainerTags []string `json:"containerTags"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - ContainerLabels: props.ContainerLabels, - ContainerTags: props.ContainerTags, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_container_test.go b/api/resource_groups_container_test.go deleted file mode 100644 index df2bf78c7..000000000 --- a/api/resource_groups_container_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// -// Author:: Darren Murray () -// 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_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 TestResourceGroupContainerGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetContainerResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleContainerResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetContainer(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Container Labels", response.Data.Props.Description) - assert.Equal(t, []string{"*"}, response.Data.Props.ContainerTags) - assert.Equal(t, []map[string]string{{"*": "*"}}, response.Data.Props.ContainerLabels) -} - -func TestResourceGroupsContainerUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateContainerResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "CONTAINER", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[\"abc123\",\"cba321\"]", "wrong container tags ids") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleContainerResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.ContainerResourceGroup, - api.ContainerResourceGroupProps{ - Description: "Updated", - ContainerTags: []string{"abc123", "cba321"}, - ContainerLabels: []map[string]string{{"label1": "testlabel"}}, - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "Container Resource Group name mismatch") - assert.Equal(t, "CONTAINER", resourceGroup.Type, "a new Container Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new Container Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateContainer(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func singleContainerResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Container Labels\",\"CONTAINER_LABELS\":[{\"*\":\"*\"}],\"CONTAINER_TAGS\":[\"*\"],\"UPDATED_BY\":null,\"LAST_UPDATED\":1586453993595}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "CONTAINER", - "enabled": 1 - } - ` -} - -func singleContainerResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Container Labels", - "containerLabels":[{"*":"*"}], - "containerTags":["*"], - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_gcp.go b/api/resource_groups_gcp.go deleted file mode 100644 index 72f27dcae..000000000 --- a/api/resource_groups_gcp.go +++ /dev/null @@ -1,165 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all gcp projects -var ( - GcpResourceGroupAllProjects = []string{"*"} -) - -// GetGcp gets a single Gcp ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetGcp(guid string) ( - response GcpResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setGcpResponse(rawResponse) -} - -// UpdateGcp updates a single Gcp ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateGcp(data ResourceGroup) ( - response GcpResourceGroupResponse, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - return -} - -// CreateGcp creates a single Gcp ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateGcp(data ResourceGroup) ( - response GcpResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - if err != nil { - return - } - - return -} - -func setGcpResponse(response resourceGroupWorkaroundResponse) (gcp GcpResourceGroupResponse, err error) { - var props GcpResourceGroupJsonStringProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - gcp = GcpResourceGroupResponse{ - Data: GcpResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - gcp.Data.Props = GcpResourceGroupProps(props) - return -} - -type GcpResourceGroupResponse struct { - Data GcpResourceGroupData `json:"data"` -} - -type GcpResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props GcpResourceGroupProps `json:"props"` -} - -type GcpResourceGroupProps struct { - Description string `json:"description,omitempty"` - Organization string `json:"organization"` - Projects []string `json:"projects"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type GcpResourceGroupJsonStringProps struct { - Description string `json:"DESCRIPTION,omitempty"` - Organization string `json:"ORGANIZATION"` - Projects []string `json:"PROJECTS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props GcpResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props GcpResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - Organization string `json:"organization"` - Projects []string `json:"projects"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - Organization: props.Organization, - Projects: props.Projects, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_gcp_test.go b/api/resource_groups_gcp_test.go deleted file mode 100644 index bc9ad9fa3..000000000 --- a/api/resource_groups_gcp_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// -// Author:: Darren Murray () -// 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_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 TestResourceGroupGcpGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetGcpResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleGcpResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetGcp(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Organizations and Projects", response.Data.Props.Description) - assert.Equal(t, []string{"*"}, response.Data.Props.Projects) - assert.Equal(t, "*", response.Data.Props.Organization) -} - -func TestResourceGroupsGcpUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateGcpResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "GCP", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[\"abc123\",\"cba321\"]", "wrong project ids") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleGcpResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.GcpResourceGroup, - api.GcpResourceGroupProps{ - Description: "Updated", - Projects: []string{"abc123", "cba321"}, - Organization: "ORG123", - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "Gcp Resource Group name mismatch") - assert.Equal(t, "GCP", resourceGroup.Type, "a new Gcp Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new Gcp Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateGcp(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func singleGcpResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Organizations and Projects\",\"PROJECTS\":[\"*\"],\"ORGANIZATION\":\"*\",\"UPDATED_BY\":null,\"LAST_UPDATED\":1586453993529}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} - -func singleGcpResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Organizations and Projects", - "organization": "*", - "projects":["*"], - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_lw_account.go b/api/resource_groups_lw_account.go deleted file mode 100644 index 5a4ee9912..000000000 --- a/api/resource_groups_lw_account.go +++ /dev/null @@ -1,161 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all lacework accounts -var ( - LwAccountResourceGroupAllAccounts = []string{"*"} -) - -// GetContainer gets a single LwAccount ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetLwAccount(guid string) ( - response LwAccountResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setLwAccountResponse(rawResponse) -} - -// UpdateLwAccount updates a single LwAccount ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateLwAccount(data ResourceGroup) ( - response LwAccountResourceGroupResponse, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - if err != nil { - return - } - - return -} - -// CreateLwAccount creates a single LwAccount ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateLwAccount(data ResourceGroup) ( - response LwAccountResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - return -} - -func setLwAccountResponse(response resourceGroupWorkaroundResponse) (lw LwAccountResourceGroupResponse, err error) { - var props LwAccountResourceGroupJsonStringProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - lw = LwAccountResourceGroupResponse{ - Data: LwAccountResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - lw.Data.Props = LwAccountResourceGroupProps(props) - return -} - -type LwAccountResourceGroupResponse struct { - Data LwAccountResourceGroupData `json:"data"` -} - -type LwAccountResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props LwAccountResourceGroupProps `json:"props"` -} - -type LwAccountResourceGroupProps struct { - Description string `json:"description,omitempty"` - LwAccounts []string `json:"lwAccounts"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type LwAccountResourceGroupJsonStringProps struct { - Description string `json:"DESCRIPTION,omitempty"` - LwAccounts []string `json:"LW_ACCOUNTS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props LwAccountResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props LwAccountResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - LwAccounts []string `json:"lwAccounts"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - LwAccounts: props.LwAccounts, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_lw_account_test.go b/api/resource_groups_lw_account_test.go deleted file mode 100644 index bd7a4aa1a..000000000 --- a/api/resource_groups_lw_account_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// -// Author:: Darren Murray () -// 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_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 TestResourceGroupLwAccountGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetLwAccountResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleLwAccountResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetLwAccount(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Lacework Accounts", response.Data.Props.Description) - assert.Equal(t, []string{"*"}, response.Data.Props.LwAccounts) -} - -func TestResourceGroupsLwAccountUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateLwAccountResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "LW_ACCOUNT", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[\"abc123\",\"cba321\"]", "wrong lw accounts ids") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleLwAccountResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.LwAccountResourceGroup, - api.LwAccountResourceGroupProps{ - Description: "Updated", - LwAccounts: []string{"abc123", "cba321"}, - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "LwAccount Resource Group name mismatch") - assert.Equal(t, "LW_ACCOUNT", resourceGroup.Type, "a new LwAccount Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new LwAccount Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateLwAccount(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func singleLwAccountResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Lacework Accounts\",\"LW_ACCOUNTS\":[\"*\"],\"UPDATED_BY\":null,\"LAST_UPDATED\":1582066544260}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "lW_ACCOUNT", - "enabled": 1 - } - ` -} - -func singleLwAccountResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Lacework Accounts", - "lwAccounts":["*"], - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_machine.go b/api/resource_groups_machine.go deleted file mode 100644 index f00ac47c7..000000000 --- a/api/resource_groups_machine.go +++ /dev/null @@ -1,163 +0,0 @@ -// -// Author:: Darren Murray () -// 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" - "strconv" - - "github.com/lacework/go-sdk/lwtime" - "github.com/pkg/errors" -) - -// Wildcard values for selecting all machine tags -var ( - MachineResourceGroupAllTags = []map[string]string{{"*": "*"}} -) - -// GetMachine gets a single Machine ResourceGroup matching the -// provided resource guid -func (svc *ResourceGroupsVersionService) GetMachine(guid string) ( - response MachineResourceGroupResponse, - err error, -) { - var rawResponse resourceGroupWorkaroundResponse - err = svc.get(guid, &rawResponse) - if err != nil { - return - } - - return setMachineAccountResponse(rawResponse) -} - -// UpdateMachine updates a single Machine ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) UpdateMachine(data ResourceGroup) ( - response MachineResourceGroupResponse, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - if err != nil { - return - } - - return -} - -// CreateMachine creates a single Machine ResourceGroup on the Lacework Server -func (svc *ResourceGroupsVersionService) CreateMachine(data ResourceGroup) ( - response MachineResourceGroupResponse, - err error, -) { - err = svc.create(data, &response) - return -} - -func setMachineAccountResponse(response resourceGroupWorkaroundResponse) ( - machine MachineResourceGroupResponse, err error, -) { - var props MachineResourceGroupJsonStringProps - - isDefault, err := strconv.Atoi(response.Data.IsDefault) - if err != nil { - return - } - - machine = MachineResourceGroupResponse{ - Data: MachineResourceGroupData{ - Guid: response.Data.Guid, - IsDefault: isDefault, - ResourceGuid: response.Data.ResourceGuid, - Name: response.Data.Name, - Type: response.Data.Type, - Enabled: response.Data.Enabled, - }, - } - - propsString, ok := response.Data.Props.(string) - if !ok { - err = errors.New("unable to cast props field from API response") - return - } - - err = json.Unmarshal([]byte(propsString), &props) - if err != nil { - return - } - machine.Data.Props = MachineResourceGroupProps(props) - return -} - -type MachineResourceGroupResponse struct { - Data MachineResourceGroupData `json:"data"` -} - -type MachineResourceGroupData struct { - Guid string `json:"guid,omitempty"` - IsDefault int `json:"isDefault,omitempty"` - ResourceGuid string `json:"resourceGuid,omitempty"` - Name string `json:"resourceName"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` - Props MachineResourceGroupProps `json:"props"` -} - -type MachineResourceGroupProps struct { - Description string `json:"description,omitempty"` - MachineTags []map[string]string `json:"machineTags"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - -// Workaround for props being returned as a json string -type MachineResourceGroupJsonStringProps struct { - Description string `json:"DESCRIPTION,omitempty"` - MachineTags []map[string]string `json:"MACHINE_TAGS"` - UpdatedBy string `json:"UPDATED_BY,omitempty"` - LastUpdated *lwtime.Epoch `json:"LAST_UPDATED,omitempty"` -} - -func (props MachineResourceGroupProps) GetBaseProps() ResourceGroupPropsBase { - return ResourceGroupPropsBase{ - Description: props.Description, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated, - } -} - -func (props MachineResourceGroupProps) MarshalJSON() ([]byte, error) { - res := struct { - Description string `json:"description,omitempty"` - MachineTags []map[string]string `json:"machineTags"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated string `json:"lastUpdated,omitempty"` - }{ - Description: props.Description, - MachineTags: props.MachineTags, - UpdatedBy: props.UpdatedBy, - LastUpdated: props.LastUpdated.String(), - } - return json.Marshal(&res) -} diff --git a/api/resource_groups_machine_test.go b/api/resource_groups_machine_test.go deleted file mode 100644 index 76371c2ec..000000000 --- a/api/resource_groups_machine_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// -// Author:: Darren Murray () -// 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_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 TestResourceGroupMachineGet(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - 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, "GetMachineResourceGroup() should be a GET method") - fmt.Fprintf(w, generateResourceGroupResponse(singleMachineResourceGroup(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - response, err := c.V2.ResourceGroups.GetMachine(resourceGUID) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) - assert.Equal(t, "group_name", response.Data.Name) - assert.Equal(t, "All Machine Tags", response.Data.Props.Description) - assert.Equal(t, []map[string]string{{"*": "*"}}, response.Data.Props.MachineTags) -} - -func TestResourceGroupsMachineUpdate(t *testing.T) { - var ( - resourceGUID = intgguid.New() - apiPath = fmt.Sprintf("ResourceGroups/%s", resourceGUID) - fakeServer = lacework.MockServer() - ) - fakeServer.MockToken("TOKEN") - defer fakeServer.Close() - - fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "PATCH", r.Method, "UpdateMachineResourceGroup() should be a PATCH method") - - if assert.NotNil(t, r.Body) { - body := httpBodySniffer(r) - assert.Contains(t, body, "group_name", "Resource Group name is missing") - assert.Contains(t, body, "MACHINE", "wrong Resource Group type") - assert.Contains(t, body, "Updated", "wrong description") - assert.Contains(t, body, "[{\"tag\":\"machineTag\"}]", "wrong machine tags") - } - - fmt.Fprintf(w, generateResourceGroupResponse(singleMachineResourceGroupUpdateResponse(resourceGUID))) - }) - - c, err := api.NewClient("test", - api.WithToken("TOKEN"), - api.WithURL(fakeServer.URL()), - ) - assert.Nil(t, err) - - resourceGroup := api.NewResourceGroup("group_name", - api.MachineResourceGroup, - api.MachineResourceGroupProps{ - Description: "Updated", - MachineTags: []map[string]string{{"tag": "machineTag"}}, - }, - ) - assert.Equal(t, "group_name", resourceGroup.Name, "Machine Resource Group name mismatch") - assert.Equal(t, "MACHINE", resourceGroup.Type, "a new Machine Resource Group should match its type") - assert.Equal(t, 1, resourceGroup.Enabled, "a new Machine Resource Group should be enabled") - resourceGroup.ResourceGuid = resourceGUID - - response, err := c.V2.ResourceGroups.UpdateMachine(&resourceGroup) - assert.Nil(t, err) - assert.NotNil(t, response) - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) -} - -func singleMachineResourceGroup(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": "1", - "props": "{\"DESCRIPTION\":\"All Machine Tags\",\"MACHINE_TAGS\":[{\"*\":\"*\"}],\"UPDATED_BY\":null,\"LAST_UPDATED\":1586453993565}", - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "MACHINE", - "enabled": 1 - } - ` -} - -func singleMachineResourceGroupUpdateResponse(id string) string { - return ` - { - "guid": "` + id + `", - "isDefault": 1, - "props": { - "description":"All Machine Tags", - "machineTags":[{"*":"*"}], - "updatedBy":null, - "lastUpdated":1586453993470 - }, - "resourceGuid": "` + id + `", - "resourceName": "group_name", - "resourceType": "AWS", - "enabled": 1 - } - ` -} diff --git a/api/resource_groups_test.go b/api/resource_groups_test.go index ee8f86410..93a87eb78 100644 --- a/api/resource_groups_test.go +++ b/api/resource_groups_test.go @@ -110,7 +110,7 @@ func TestResourceGroupGet(t *testing.T) { err := c.V2.ResourceGroups.Get(resourceGUID, &response) assert.Nil(t, err) if assert.NotNil(t, response) { - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) + assert.Equal(t, resourceGUID, response.Data.ResourceGroupGuid) assert.Equal(t, "group_name", response.Data.Name) assert.Equal(t, "VANILLA", response.Data.Type) } @@ -173,7 +173,7 @@ func TestResourceGroupsDelete(t *testing.T) { err := c.V2.ResourceGroups.Get(resourceGUID, &response) assert.Nil(t, err) if assert.NotNil(t, response) { - assert.Equal(t, resourceGUID, response.Data.ResourceGuid) + assert.Equal(t, resourceGUID, response.Data.ResourceGroupGuid) assert.Equal(t, "group_name", response.Data.Name) assert.Equal(t, "VANILLA", response.Data.Type) } @@ -241,7 +241,7 @@ func TestResourceGroupsList(t *testing.T) { assert.NotNil(t, response) assert.Equal(t, expectedLen, len(response.Data)) for _, d := range response.Data { - assert.Contains(t, allGuids, d.ResourceGuid) + assert.Contains(t, allGuids, d.ResourceGroupGuid) } } @@ -257,8 +257,6 @@ func generateResourceGroups(guids []string, iType string) string { resourceGroups[i] = singleContainerResourceGroup(guid) case api.GcpResourceGroup.String(): resourceGroups[i] = singleGcpResourceGroup(guid) - case api.LwAccountResourceGroup.String(): - resourceGroups[i] = singleLwAccountResourceGroup(guid) case api.MachineResourceGroup.String(): resourceGroups[i] = singleMachineResourceGroup(guid) } @@ -266,6 +264,332 @@ func generateResourceGroups(guids []string, iType string) string { return strings.Join(resourceGroups, ", ") } +func singleAwsResourceGroup(id string) string { + return ` + { + "guid": "` + id + `", + "description": "All Aws Resources" + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "AWS", + "enabled": 1, + "query": "{ + "filters": { + "filter1": { + "field": "AWS_ACCOUNT_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "AWS_ORGANIZATION_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "AWS_RESOURCE_TAGS", + "operation": "EQUALS", + "key": "*", + "values": [ + "*" + ] + }, + "filter4": { + "field": "AWS_RESOURCE_REGION", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + } + ] + } + }" + } + ` +} + +func singleAzureResourceGroup(id string) string { + + return ` + { + "guid": "` + id + `", + "description": "All Azure Resources" + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "AZURE", + "enabled": 1, + "query": "{ + "filters": { + "filter1": { + "field": "AZURE_TENANT_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "AZURE_SUBSCRIPTION_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "AZURE_RESOURCE_TAGS", + "operation": "EQUALS", + "key": "*", + "values": [ + "*" + ] + }, + "filter4": { + "field": "AZURE_RESOURCE_REGION", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter5": { + "field": "AZURE_TENANT_NAME", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter6": { + "field": "AZURE_SUBSCRIPTION_NAME", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + }, + { + "filterName": "filter5" + }, + { + "filterName": "filter6" + } + ] + } + }" + } + ` +} + +func singleContainerResourceGroup(id string) string { + return ` + { + "guid": "` + id + `", + "description": "All Container Resources" + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "CONTAINER", + "enabled": 1, + "query": "{ + "filters": { + "filter1": { + "field": "IMAGE_TAG", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "CONTAINER_LABELS", + "operation": "EQUALS", + "values": [ + "*" + ], + "key": "*" + }, + "filter3": { + "field": "IMAGE_REPO", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter4": { + "field": "IMAGE_REGISTRY", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + } + ] + } + }" + } + ` +} + +func singleGcpResourceGroup(id string) string { + return ` + { + "guid": "` + id + `", + "description": "All GCP Resources" + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "GCP", + "enabled": 1, + "query": "{ + "filters": { + "filter1": { + "field": "GCP_ORGANIZATION_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "GCP_FOLDER_IDS", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "GCP_PROJECT_ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter4": { + "field": "GCP_RESOURCE_TAGS", + "operation": "EQUALS", + "key": "*", + "values": [ + "*" + ] + }, + "filter5": { + "field": "GCP_RESOURCE_REGION", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter6": { + "field": "GCP_ORGANIZATION_NAME", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + }, + { + "filterName": "filter5" + }, + { + "filterName": "filter5" + } + ] + } + }" + } + ` +} + +func singleMachineResourceGroup(id string) string { + return ` + { + "guid": "` + id + `", + "description": "All Machine Resources" + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "MACHINE", + "enabled": 1, + "query": "{ + "filters": { + "filter1": { + "field": "MACHINE_TAGS", + "operation": "EQUALS", + "values": [ + "*" + ], + "key": "*" + } + }, + "expression": { + "filterName": "filter1" + } + }" + } + ` +} + func generateResourceGroupsResponse(data string) string { return ` { @@ -282,19 +606,19 @@ func generateResourceGroupResponse(data string) string { ` } -func singleVanillaResourceGroup(id string, iType string, props string) string { - if props == "" { - props = "{}" +func singleVanillaResourceGroup(id string, iType string, query string) string { + if query == "" { + query = "{}" } return ` { "guid": "` + id + `", - "isDefault": "1", - "props": ` + props + `, - "resourceGuid": "` + id + `", - "resourceName": "group_name", + "isDefaultBoolean": "true", + "query": ` + query + `, + "resourceGroupGuid": "` + id + `", + "name": "group_name", "resourceType": "` + iType + `", - "enabled": 1 + "enabled": 1, } ` } diff --git a/api/resource_groups_v2.go b/api/resource_groups_v2.go index e438d1871..5b415d756 100644 --- a/api/resource_groups_v2.go +++ b/api/resource_groups_v2.go @@ -172,9 +172,6 @@ func (group ResourceGroupDataWithQuery) ID() string { return group.ResourceGroupGuid } -func (group *ResourceGroupDataWithQuery) ResetRGV2Fields() { - // no-op -} func (group *ResourceGroupDataWithQuery) ResetResourceGUID() { group.ResourceGroupGuid = "" diff --git a/api/resource_groups_version_service.go b/api/resource_groups_version_service.go deleted file mode 100644 index 8af98aa41..000000000 --- a/api/resource_groups_version_service.go +++ /dev/null @@ -1,373 +0,0 @@ -// -// Author:: Zeki Sherif() -// 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" - "fmt" - "strconv" - - "github.com/pkg/errors" -) - -type ResourceGroupsVersionService struct { - client *Client - v1ResourceGroupService *ResourceGroupsService - v2ResourceGroupService *ResourceGroupsV2Service - featureFlagService *FeatureFlagsService -} - -type ResourceGroupsInterfaceData interface { - GetProps() interface{} - GetQuery() *RGQuery -} - -func (group ResourceGroupData) GetProps() interface{} { - return group.Props -} - -func (group ResourceGroupData) GetQuery() *RGQuery { - return nil -} - -func NewResourceGroupsVersionService(c *Client) *ResourceGroupsVersionService { - return &ResourceGroupsVersionService{ - c, - &ResourceGroupsService{c}, - &ResourceGroupsV2Service{c}, - &FeatureFlagsService{c}, - } -} - -// NewResourceGroup returns an instance of the ResourceGroupData struct with the -// provided ResourceGroup type, name and the props field as an interface{}. -// -// NOTE: This function must be used by any ResourceGroup type. -// -// Basic usage: Initialize a new ContainerResourceGroup struct, then -// -// use the new instance to do CRUD operations -// -// client, err := api.NewClient("account") -// if err != nil { -// return err -// } -// -// group := api.NewResourceGroup("container resource group", -// api.ContainerResourceGroup, -// api.ContainerResourceGroupData{ -// Props: api.ContainerResourceGroupProps{ -// Description: "all containers, -// ContainerLabels: ContainerResourceGroupAllLabels, -// ContainerTags: ContainerResourceGroupAllTags, -// }, -// }, -// ) -// -// client.V2.ResourceGroups.Create(group) -func NewResourceGroup(name string, iType resourceGroupType, props interface{}) ResourceGroupData { - return ResourceGroupData{ - Name: name, - Type: iType.String(), - Enabled: 1, - Props: props, - } -} - -// NewResourceGroupWithQuery Only available with RGv2 beta -func NewResourceGroupWithQuery(name string, iType resourceGroupType, - description string, query *RGQuery) ResourceGroupDataWithQuery { - return ResourceGroupDataWithQuery{ - Name: name, - Type: iType.String(), - Enabled: 1, - Query: query, - Description: description, - } -} - -func isRGV2FlagEnabled(featureFlagService *FeatureFlagsService) bool { - response, err := featureFlagService.GetFeatureFlagsMatchingPrefix(ApiV2CliFeatureFlag) - - if err != nil { - return false - } - - return len(response.Data.Flags) >= 1 -} - -func (svc *ResourceGroupsVersionService) Get(guid string, response interface{}) error { - var rawResponse resourceGroupWorkaroundResponse - err := svc.get(guid, &rawResponse) - if err != nil { - return err - } - - if rawResponse.Data.Query != nil { - return castRGV2WorkAroundResponse(rawResponse, response) - } else { - return castRGV1WorkAroundResponse(rawResponse, response) - } -} - -func (svc *ResourceGroupsVersionService) Create(group ResourceGroupsInterfaceData) ( - response ResourceGroupResponse, - err error, -) { - isV2FlagEnabled := isRGV2FlagEnabled(svc.featureFlagService) - - if group.GetProps() == nil && !isV2FlagEnabled && group.GetQuery() == nil { - if isV2FlagEnabled { - err = errors.New("Invalid request. Missing `query` field.") - } else { - err = errors.New("Invalid request. Missing `props` field.") - } - - return - } - - if group.GetProps() != nil { - response, err = svc.v1ResourceGroupService.Create(group.(ResourceGroupData)) - return - } - - createResponse, createErr := svc.v2ResourceGroupService.Create(group.(ResourceGroupDataWithQuery)) - if createErr != nil { - err = createErr - return - } - - err = castResourceGroupV2Response(createResponse, &response) - return -} - -func (svc *ResourceGroupsVersionService) Update(group ResourceGroupsInterfaceData) ( - response ResourceGroupResponse, - err error, -) { - if group.GetProps() != nil { - response, err = svc.v1ResourceGroupService.Update(group.(ResourceGroup)) - return - } - - isV2FlagEnabled := isRGV2FlagEnabled(svc.featureFlagService) - - if isV2FlagEnabled { - updateResponse, updateErr := svc.v2ResourceGroupService.Update(group.(ResourceGroup)) - if updateErr != nil { - err = updateErr - return - } - - err = castResourceGroupV2Response(updateResponse, &response) - return - } - - err = errors.New("Unable to update resource group") - return -} - -func (svc *ResourceGroupsVersionService) Delete(guid string) error { - // It doesn't matcher which version of service we use as api-server handles - // delete for both v1 and v2 resource groups - err := svc.v1ResourceGroupService.Delete(guid) - - if err != nil { - return err - } - - return nil -} - -func (svc *ResourceGroupsVersionService) List() (response ResourceGroupsResponse, err error) { - var rawResponse resourceGroupsWorkaroundResponse - err = svc.client.RequestDecoder("GET", apiV2ResourceGroups, nil, &rawResponse) - - if err != nil { - return - } - - return setResourceGroupsVersionUnawareResponse(rawResponse) -} - -func castRGV1WorkAroundResponse(data resourceGroupWorkaroundResponse, response interface{}) error { - isDefault, err := strconv.Atoi(data.Data.IsDefault) - if err != nil { - return err - } - group := ResourceGroupResponse{ - Data: ResourceGroupData{ - Guid: data.Data.Guid, - IsDefault: isDefault, - ResourceGuid: data.Data.ResourceGuid, - Name: data.Data.Name, - Type: data.Data.Type, - Enabled: data.Data.Enabled, - Props: data.Data.Props, - }, - } - - j, err := json.Marshal(group) - if err != nil { - return err - } - - err = json.Unmarshal(j, &response) - if err != nil { - return err - } - - return nil -} - -func castRGV2WorkAroundResponse(data resourceGroupWorkaroundResponse, response interface{}) error { - group := ResourceGroupResponse{ - Data: ResourceGroupData{ - Type: data.Data.Type, - Enabled: data.Data.Enabled, - NameV2: data.Data.NameV2, - Query: data.Data.Query, - Description: data.Data.Description, - ResourceGroupGuid: data.Data.ResourceGroupGuid, - CreatedTime: data.Data.CreatedTime, - CreatedBy: data.Data.CreatedBy, - UpdatedTime: data.Data.UpdatedTime, - UpdatedBy: data.Data.UpdatedBy, - IsDefaultBoolean: data.Data.IsDefaultBoolean, - IsOrg: data.Data.IsOrg, - }, - } - - j, err := json.Marshal(group) - if err != nil { - return err - } - - err = json.Unmarshal(j, &response) - if err != nil { - return err - } - - return nil -} - -func castResourceGroupV2Response(data ResourceGroupV2Response, response interface{}) error { - group := ResourceGroupResponse{ - Data: ResourceGroupData{ - Type: data.Data.Type, - Enabled: data.Data.Enabled, - NameV2: data.Data.Name, - Query: data.Data.Query, - Description: data.Data.Description, - ResourceGroupGuid: data.Data.ResourceGroupGuid, - CreatedTime: data.Data.CreatedTime, - CreatedBy: data.Data.CreatedBy, - UpdatedTime: data.Data.UpdatedTime, - UpdatedBy: data.Data.UpdatedBy, - IsDefaultBoolean: data.Data.IsDefaultBoolean, - IsOrg: data.Data.IsOrg, - }, - } - - j, err := json.Marshal(group) - if err != nil { - return err - } - - err = json.Unmarshal(j, &response) - if err != nil { - return err - } - - return nil -} - -func (svc *ResourceGroupsVersionService) get(guid string, response interface{}) error { - if guid == "" { - return errors.New("specify an resourceGuid") - } - apiPath := fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid) - return svc.client.RequestDecoder("GET", apiPath, nil, response) -} - -func (svc *ResourceGroupsVersionService) create(data interface{}, response interface{}) error { - return svc.client.RequestEncoderDecoder("POST", apiV2ResourceGroups, data, response) -} - -func (svc *ResourceGroupsVersionService) update(guid string, data interface{}, response interface{}) error { - if guid == "" { - return errors.New("specify a resource group guid") - } - - apiPath := fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid) - return svc.client.RequestEncoderDecoder("PATCH", apiPath, data, response) -} - -func setResourceGroupResponse(response resourceGroupWorkaroundData) (ResourceGroupResponse, - error) { - - if response.Props != nil { - isDefault, err := strconv.Atoi(response.IsDefault) - if err != nil { - return ResourceGroupResponse{}, err - } - return ResourceGroupResponse{ - Data: ResourceGroupData{ - Guid: response.Guid, - IsDefault: isDefault, - ResourceGuid: response.ResourceGuid, - Name: response.Name, - Type: response.Type, - Enabled: response.Enabled, - Props: response.Props, - }, - }, nil - } else { - return ResourceGroupResponse{ - Data: ResourceGroupData{ - Type: response.Type, - Enabled: response.Enabled, - NameV2: response.NameV2, - Query: response.Query, - Description: response.Description, - ResourceGroupGuid: response.ResourceGroupGuid, - CreatedTime: response.CreatedTime, - CreatedBy: response.CreatedBy, - UpdatedTime: response.UpdatedTime, - UpdatedBy: response.UpdatedBy, - IsDefaultBoolean: response.IsDefaultBoolean, - IsOrg: response.IsOrg, - }, - }, nil - } -} - -func setResourceGroupsVersionUnawareResponse(workaround resourceGroupsWorkaroundResponse) ( - ResourceGroupsResponse, error) { - var data []ResourceGroupData - for _, r := range workaround.Data { - group, err := setResourceGroupResponse(r) - if err != nil { - return ResourceGroupsResponse{}, err - } - data = append(data, group.Data) - } - - return ResourceGroupsResponse{Data: data}, nil -} diff --git a/api/v2.go b/api/v2.go index bf47f1e21..b616ff1f1 100644 --- a/api/v2.go +++ b/api/v2.go @@ -43,7 +43,7 @@ type V2Endpoints struct { ContainerRegistries *ContainerRegistriesService Configs *v2ConfigService FeatureFlags *FeatureFlagsService - ResourceGroups *ResourceGroupsVersionService + ResourceGroups *ResourceGroupsV2Service AgentAccessTokens *AgentAccessTokensService AgentInfo *AgentInfoService Inventory *InventoryService @@ -80,7 +80,7 @@ func NewV2Endpoints(c *Client) *V2Endpoints { &ContainerRegistriesService{c}, NewV2ConfigService(c), &FeatureFlagsService{c}, - NewResourceGroupsVersionService(c), + &ResourceGroupsV2Service{c}, &AgentAccessTokensService{c}, &AgentInfoService{c}, &InventoryService{c}, diff --git a/cli/cmd/resource_group_aws.go b/cli/cmd/resource_group_aws.go deleted file mode 100644 index dcdafc69c..000000000 --- a/cli/cmd/resource_group_aws.go +++ /dev/null @@ -1,95 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "strings" - - "github.com/AlecAivazis/survey/v2" - - "github.com/lacework/go-sdk/api" -) - -func createAwsResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "account_ids", - Prompt: &survey.Multiline{Message: "List of Account IDs: "}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - AccountIDs string `survey:"account_ids"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - aws := api.NewResourceGroup( - answers.Name, - api.AwsResourceGroup, - api.AwsResourceGroupProps{ - Description: answers.Description, - AccountIDs: strings.Split(answers.AccountIDs, "\n"), - }) - - cli.StartProgress(" Creating resource group...") - _, err = cli.LwApi.V2.ResourceGroups.Create(aws) - cli.StopProgress() - return err -} - -func setAwsProps(group []byte) []string { - awsProps, err := unmarshallAwsProps(group) - if err != nil { - return []string{} - } - - return []string{"ACCOUNT IDS", strings.Join(awsProps.AccountIDs, ",")} -} - -func unmarshallAwsPropString(group []byte) (props api.AwsResourceGroupProps, err error) { - var rawProps api.AwsResourceJsonStringGroupProps - err = json.Unmarshal(group, &rawProps) - props = api.AwsResourceGroupProps(rawProps) - return -} - -func unmarshallAwsProps(group []byte) (props api.AwsResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_azure.go b/cli/cmd/resource_group_azure.go deleted file mode 100644 index 945bd9f15..000000000 --- a/cli/cmd/resource_group_azure.go +++ /dev/null @@ -1,106 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "strings" - - "github.com/AlecAivazis/survey/v2" - - "github.com/lacework/go-sdk/api" -) - -func createAzureResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "tenant", - Prompt: &survey.Input{Message: "Tenant: "}, - Validate: survey.Required, - }, - { - Name: "subscriptions", - Prompt: &survey.Multiline{Message: "List of Subscriptions: "}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - Tenant string `survey:"tenant"` - Subscriptions string `survey:"subscriptions"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - azure := api.NewResourceGroup( - answers.Name, - api.AzureResourceGroup, - api.AzureResourceGroupProps{ - Description: answers.Description, - Tenant: answers.Tenant, - Subscriptions: strings.Split(answers.Subscriptions, "\n"), - }) - - cli.StartProgress(" Creating resource group...") - _, err = cli.LwApi.V2.ResourceGroups.Create(azure) - cli.StopProgress() - return err -} - -func setAzureProps(group []byte) [][]string { - var details [][]string - - azProps, err := unmarshallAzureProps(group) - if err != nil { - return [][]string{} - } - - details = append(details, []string{"TENANT", azProps.Tenant}) - details = append(details, []string{"SUBSCRIPTIONS", strings.Join(azProps.Subscriptions, ",")}) - return details -} - -func unmarshallAzurePropString(group []byte) (props api.AzureResourceGroupProps, err error) { - var rawProps api.AzureResourceJsonStringGroupProps - err = json.Unmarshal(group, &rawProps) - props = api.AzureResourceGroupProps(rawProps) - return -} - -func unmarshallAzureProps(group []byte) (props api.AzureResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_container.go b/cli/cmd/resource_group_container.go deleted file mode 100644 index e1ce514bf..000000000 --- a/cli/cmd/resource_group_container.go +++ /dev/null @@ -1,114 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/AlecAivazis/survey/v2" - - "github.com/lacework/go-sdk/api" -) - -func createContainerResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "tags", - Prompt: &survey.Multiline{Message: "List of Tags: "}, - Validate: survey.Required, - }, { - Name: "labels", - Prompt: &survey.Multiline{Message: "List of 'key:value' Labels:"}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - Tags string `survey:"tags"` - Labels string `survey:"labels"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - container := api.NewResourceGroup( - answers.Name, - api.ContainerResourceGroup, - api.ContainerResourceGroupProps{ - Description: answers.Description, - ContainerTags: strings.Split(answers.Tags, "\n"), - ContainerLabels: castStringToLimitByLabel(answers.Labels), - }) - - cli.StartProgress(" Creating resource group...") - _, err = cli.LwApi.V2.ResourceGroups.Create(container) - cli.StopProgress() - return err -} - -func setContainerProps(group []byte) [][]string { - var ( - details [][]string - labels []string - ) - - ctrProps, err := unmarshallContainerProps(group) - if err != nil { - return [][]string{} - } - - for _, labelMap := range ctrProps.ContainerLabels { - for key, val := range labelMap { - labels = append(labels, fmt.Sprintf("%s: %v", key, val)) - } - } - details = append(details, []string{"CONTAINER LABELS", strings.Join(labels, ",")}) - details = append(details, []string{"CONTAINER TAGS", strings.Join(ctrProps.ContainerTags, ",")}) - return details -} - -func unmarshallContainerPropString(group []byte) (props api.ContainerResourceGroupProps, err error) { - var rawProps api.ContainerResourceJsonStringGroupProps - err = json.Unmarshal(group, &rawProps) - props = api.ContainerResourceGroupProps(rawProps) - return -} - -func unmarshallContainerProps(group []byte) (props api.ContainerResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_gcp.go b/cli/cmd/resource_group_gcp.go deleted file mode 100644 index 7549b1874..000000000 --- a/cli/cmd/resource_group_gcp.go +++ /dev/null @@ -1,105 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "strings" - - "github.com/AlecAivazis/survey/v2" - "github.com/lacework/go-sdk/api" -) - -func createGcpResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "organization", - Prompt: &survey.Input{Message: "Organization: "}, - Validate: survey.Required, - }, - { - Name: "projects", - Prompt: &survey.Multiline{Message: "List of Projects: "}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - Organization string `survey:"organization"` - Projects string `survey:"projects"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - gcp := api.NewResourceGroup( - answers.Name, - api.GcpResourceGroup, - api.GcpResourceGroupProps{ - Description: answers.Description, - Organization: answers.Organization, - Projects: strings.Split(answers.Projects, "\n"), - }) - - cli.StartProgress(" Creating resource group...") - _, err = cli.LwApi.V2.ResourceGroups.Create(gcp) - cli.StopProgress() - return err -} - -func setGcpProps(group []byte) [][]string { - var details [][]string - - gcpProps, err := unmarshallGcpProps(group) - if err != nil { - return [][]string{} - } - - details = append(details, []string{"ORGANIZATION", gcpProps.Organization}) - details = append(details, []string{"PROJECTS", strings.Join(gcpProps.Projects, ",")}) - return details -} - -func unmarshallGcpPropString(group []byte) (props api.GcpResourceGroupProps, err error) { - var rawProps api.GcpResourceGroupJsonStringProps - err = json.Unmarshal(group, &rawProps) - props = api.GcpResourceGroupProps(rawProps) - return -} - -func unmarshallGcpProps(group []byte) (props api.GcpResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_lw_account.go b/cli/cmd/resource_group_lw_account.go deleted file mode 100644 index 252572a27..000000000 --- a/cli/cmd/resource_group_lw_account.go +++ /dev/null @@ -1,103 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "strings" - - "github.com/AlecAivazis/survey/v2" - "github.com/lacework/go-sdk/api" -) - -func createLwAccountResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "accounts", - Prompt: &survey.Multiline{Message: "List of Lacework Accounts: "}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - Accounts string `survey:"accounts"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - lwAccount := api.NewResourceGroup( - answers.Name, - api.LwAccountResourceGroup, - api.LwAccountResourceGroupProps{ - Description: answers.Description, - LwAccounts: strings.Split(answers.Accounts, "\n"), - }) - - orgLwClient, err := api.CopyClient(cli.LwApi, - api.WithOrgAccess(), - ) - - if err != nil { - return err - } - - cli.StartProgress(" Creating resource group...") - _, err = orgLwClient.V2.ResourceGroups.Create(lwAccount) - - cli.StopProgress() - return err -} - -func setLwAccountProps(group []byte) []string { - lwProps, err := unmarshallLwAccountProps(group) - if err != nil { - return []string{} - } - - return []string{"LW ACCOUNTS", strings.Join(lwProps.LwAccounts, ",")} -} - -func unmarshallLwAccountPropString(group []byte) (props api.LwAccountResourceGroupProps, err error) { - var rawProps api.LwAccountResourceGroupJsonStringProps - err = json.Unmarshal(group, &rawProps) - props = api.LwAccountResourceGroupProps(rawProps) - return -} - -func unmarshallLwAccountProps(group []byte) (props api.LwAccountResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_machine.go b/cli/cmd/resource_group_machine.go deleted file mode 100644 index 78083d0f5..000000000 --- a/cli/cmd/resource_group_machine.go +++ /dev/null @@ -1,102 +0,0 @@ -// -// Author:: Darren Murray() -// 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 cmd - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/AlecAivazis/survey/v2" - "github.com/lacework/go-sdk/api" -) - -func createMachineResourceGroup() error { - questions := []*survey.Question{ - { - Name: "name", - Prompt: &survey.Input{Message: "Name: "}, - Validate: survey.Required, - }, - { - Name: "description", - Prompt: &survey.Input{Message: "Description: "}, - Validate: survey.Required, - }, - { - Name: "tags", - Prompt: &survey.Multiline{Message: "List of 'key:value' Machine Tags:"}, - Validate: survey.Required, - }, - } - - answers := struct { - Name string - Description string `survey:"description"` - MachineTags string `survey:"tags"` - }{} - - err := survey.Ask(questions, &answers, - survey.WithIcons(promptIconsFunc), - ) - if err != nil { - return err - } - - machine := api.NewResourceGroup( - answers.Name, - api.MachineResourceGroup, - api.MachineResourceGroupProps{ - Description: answers.Description, - MachineTags: castStringToLimitByLabel(answers.MachineTags), - }) - - cli.StartProgress(" Creating resource group...") - _, err = cli.LwApi.V2.ResourceGroups.Create(machine) - cli.StopProgress() - return err -} - -func setMachineProps(group []byte) []string { - machineProps, err := unmarshallMachineProps(group) - if err != nil { - return []string{} - } - - var tags []string - for _, tagMap := range machineProps.MachineTags { - for key, val := range tagMap { - tags = append(tags, fmt.Sprintf("%s: %v", key, val)) - - } - } - return []string{"MACHINE TAGS", strings.Join(tags, ",")} -} - -func unmarshallMachinePropString(group []byte) (props api.MachineResourceGroupProps, err error) { - var rawProps api.MachineResourceGroupJsonStringProps - err = json.Unmarshal(group, &rawProps) - props = api.MachineResourceGroupProps(rawProps) - return -} - -func unmarshallMachineProps(group []byte) (props api.MachineResourceGroupProps, err error) { - err = json.Unmarshal(group, &props) - return -} diff --git a/cli/cmd/resource_group_v2.go b/cli/cmd/resource_group_v2.go index 2e5b05a35..ef01b9f37 100644 --- a/cli/cmd/resource_group_v2.go +++ b/cli/cmd/resource_group_v2.go @@ -71,8 +71,7 @@ func createResourceGroupV2(resourceType string) error { // This should never reach this. The type is controlled by us in cmd/resource_groups return errors.New("internal error") } - resourceGroup := api.NewResourceGroupWithQuery(answers.Name, groupType, answers.Description, &rgQuery) - + resourceGroup := api.NewResourceGroup(answers.Name, groupType, answers.Description, &rgQuery) cli.StartProgress(" Creating resource group...") _, err = cli.LwApi.V2.ResourceGroups.Create(resourceGroup) cli.StopProgress() diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index cb0e896c3..630495522 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -19,14 +19,13 @@ package cmd import ( - "encoding/json" - "fmt" - "time" + "fmt" + "strconv" +"time" "github.com/AlecAivazis/survey/v2" "github.com/lacework/go-sdk/api" - "github.com/olekukonko/tablewriter" - "github.com/pkg/errors" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -70,34 +69,13 @@ Then navigate to Settings > Resource Groups. groups := make([]resourceGroup, 0) for _, g := range resourceGroups.Data { - var props api.ResourceGroupProps - var resourceGroupGuid string - var isDefault int - var resourceName string - - if g.GetProps() != nil { - props, _ = parsePropsType(g.Props, g.Type) - resourceGroupGuid = g.ResourceGuid - isDefault = g.IsDefault - resourceName = g.Name - } else { - resourceGroupGuid = g.ResourceGroupGuid - if *g.IsDefaultBoolean { - isDefault = 1 - } else { - isDefault = 0 - } - resourceName = g.NameV2 - } groups = append(groups, resourceGroup{ - Id: resourceGroupGuid, + Id: g.ResourceGroupGuid, ResType: g.Type, - Name: resourceName, - status: g.Status(), - Props: props, + Name: g.Name, Enabled: g.Enabled, - IsDefault: isDefault, + IsDefaultBoolean: *g.IsDefaultBoolean, Query: g.Query, }) } @@ -111,7 +89,8 @@ Then navigate to Settings > Resource Groups. rows := [][]string{} for _, g := range groups { - rows = append(rows, []string{g.Id, g.ResType, g.Name, g.status, IsDefault(g.IsDefault)}) + rows = append(rows, []string{g.Id, g.ResType, g.Name, strconv.Itoa(g.Enabled), + strconv.FormatBool(g.IsDefaultBoolean)}) } cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "STATUS", "DEFAULT"}, rows)) @@ -131,34 +110,12 @@ Then navigate to Settings > Resource Groups. return errors.Wrap(err, "unable to get resource group") } - var props api.ResourceGroupProps - var resourceGroupGuid string - var isDefault int - var name string - - if response.Data.Props != nil { - props, _ = parsePropsType(response.Data.Props, response.Data.Type) - resourceGroupGuid = response.Data.ResourceGuid - isDefault = response.Data.IsDefault - name = response.Data.Name - } else { - resourceGroupGuid = response.Data.ResourceGroupGuid - if *response.Data.IsDefaultBoolean { - isDefault = 1 - } else { - isDefault = 0 - } - name = response.Data.NameV2 - } - group := resourceGroup{ - Id: resourceGroupGuid, + Id: response.Data.ResourceGroupGuid, ResType: response.Data.Type, - Name: name, - status: response.Data.Status(), - Props: props, + Name: response.Data.Name, Enabled: response.Data.Enabled, - IsDefault: isDefault, + IsDefaultBoolean: *response.Data.IsDefaultBoolean, Query: response.Data.Query, Description: response.Data.Description, UpdatedBy: response.Data.UpdatedBy, @@ -176,15 +133,18 @@ Then navigate to Settings > Resource Groups. if group.Props != nil { groupCommon = append(groupCommon, - []string{group.Id, group.ResType, group.Name, group.status, IsDefault(group.IsDefault)}, + []string{group.Id, group.ResType, group.Name, strconv.Itoa(group.Enabled), + strconv.FormatBool(group.IsDefaultBoolean)}, ) - cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "STATE", "DEFAULT"}, groupCommon)) + cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP GUID", "TYPE", "NAME", "ENABLED", + "IS_DEFAULT_BOOLEAN"}, + groupCommon)) cli.OutputHuman("\n") - cli.OutputHuman(buildResourceGroupPropsTable(group)) + //cli.OutputHuman(buildResourceGroupPropsTable(group)) } else { groupCommon = append(groupCommon, - []string{group.Id, group.ResType, group.Name, group.Description, group.status, - IsDefault(group.IsDefault), group.UpdatedBy, group.UpdatedTime.UTC().String()}, + []string{group.Id, group.ResType, group.Name, group.Description, strconv.Itoa(group.Enabled), + strconv.FormatBool(group.IsDefaultBoolean), group.UpdatedBy, group.UpdatedTime.UTC().String()}, ) cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "DESCRIPTION", "STATE", "DEFAULT", "UPDATED BY", "UPDATED_TIME"}, groupCommon)) @@ -231,28 +191,6 @@ Then navigate to Settings > Resource Groups. }, } ) - -// parsePropsType converts props json string to interface of resource group props type -func parsePropsType(props interface{}, resourceType string) (api.ResourceGroupProps, error) { - propsString := props.(string) - - switch resourceType { - case api.AwsResourceGroup.String(): - return unmarshallAwsPropString([]byte(propsString)) - case api.AzureResourceGroup.String(): - return unmarshallAzurePropString([]byte(propsString)) - case api.ContainerResourceGroup.String(): - return unmarshallContainerPropString([]byte(propsString)) - case api.GcpResourceGroup.String(): - return unmarshallGcpPropString([]byte(propsString)) - case api.LwAccountResourceGroup.String(): - return unmarshallLwAccountPropString([]byte(propsString)) - case api.MachineResourceGroup.String(): - return unmarshallMachinePropString([]byte(propsString)) - } - return nil, errors.New("Unable to determine resource group props type") -} - func promptCreateResourceGroup() error { resourceGroupOptions := []string{ @@ -260,25 +198,9 @@ func promptCreateResourceGroup() error { "AZURE", "CONTAINER", "GCP", - "LW_ACCOUNT", "MACHINE", - } - - isRGv2Enabled := false - ffResponse, _ := cli.LwApi.V2.FeatureFlags.GetFeatureFlagsMatchingPrefix(api.ApiV2CliFeatureFlag) - if len(ffResponse.Data.Flags) >= 1 { - isRGv2Enabled = true - } - - if isRGv2Enabled { - resourceGroupOptions = append(resourceGroupOptions, - "AWS(v2)", - "AZURE(v2)", - "GCP(v2)", - "CONTAINER(v2)", - "MACHINE(v2)", - "OCI(v2)", - ) + "OCI", + "KUBERNETES", } var ( @@ -295,77 +217,67 @@ func promptCreateResourceGroup() error { switch group { case "AWS": - return createAwsResourceGroup() - case "AZURE": - return createAzureResourceGroup() - case "CONTAINER": - return createContainerResourceGroup() - case "GCP": - return createGcpResourceGroup() - case "LW_ACCOUNT": - return createLwAccountResourceGroup() - case "MACHINE": - return createMachineResourceGroup() - case "AWS(v2)": return createResourceGroupV2("AWS") - case "AZURE(v2)": + case "AZURE": return createResourceGroupV2("AZURE") - case "GCP(v2)": + case "GCP": return createResourceGroupV2("GCP") - case "CONTAINER(v2)": + case "CONTAINER": return createResourceGroupV2("CONTAINER") - case "MACHINE(v2)": + case "MACHINE": return createResourceGroupV2("MACHINE") - case "OCI(v2)": + case "OCI": return createResourceGroupV2("OCI") + case "KUBERNETES": + return createResourceGroupV2("KUBERNETES") default: return errors.New("unknown resource group type") } } -func buildResourceGroupPropsTable(group resourceGroup) string { - props := determineResourceGroupProps(group.ResType, group.Props) - - return renderOneLineCustomTable("RESOURCE GROUP PROPS", - renderCustomTable([]string{}, props, - tableFunc(func(t *tablewriter.Table) { - t.SetBorder(false) - t.SetColumnSeparator(" ") - t.SetAutoWrapText(false) - t.SetAlignment(tablewriter.ALIGN_LEFT) - }), - ), - tableFunc(func(t *tablewriter.Table) { - t.SetBorder(false) - t.SetAutoWrapText(false) - }), - ) -} - -func determineResourceGroupProps(resType string, props api.ResourceGroupProps) [][]string { - propsString, err := json.Marshal(props) - if err != nil { - return [][]string{} - } - details := setBaseProps(props) - - switch resType { - case api.AwsResourceGroup.String(): - details = append(details, setAwsProps(propsString)) - case api.AzureResourceGroup.String(): - details = append(details, setAzureProps(propsString)...) - case api.ContainerResourceGroup.String(): - details = append(details, setContainerProps(propsString)...) - case api.GcpResourceGroup.String(): - details = append(details, setGcpProps(propsString)...) - case api.LwAccountResourceGroup.String(): - details = append(details, setLwAccountProps(propsString)) - case api.MachineResourceGroup.String(): - details = append(details, setMachineProps(propsString)) - } - - return details -} +//func buildResourceGroupPropsTable(group resourceGroup) string { +// props := determineResourceGroupProps(group.ResType, group.Props) +// +// return renderOneLineCustomTable("RESOURCE GROUP PROPS", +// renderCustomTable([]string{}, props, +// tableFunc(func(t *tablewriter.Table) { +// t.SetBorder(false) +// t.SetColumnSeparator(" ") +// t.SetAutoWrapText(false) +// t.SetAlignment(tablewriter.ALIGN_LEFT) +// }), +// ), +// tableFunc(func(t *tablewriter.Table) { +// t.SetBorder(false) +// t.SetAutoWrapText(false) +// }), +// ) +//} + +//func determineResourceGroupProps(resType string, props api.ResourceGroupProps) [][]string { +// propsString, err := json.Marshal(props) +// if err != nil { +// return [][]string{} +// } +// details := setBaseProps(props) +// +// switch resType { +// case api.AwsResourceGroup.String(): +// details = append(details, setAwsProps(propsString)) +// case api.AzureResourceGroup.String(): +// details = append(details, setAzureProps(propsString)...) +// case api.ContainerResourceGroup.String(): +// details = append(details, setContainerProps(propsString)...) +// case api.GcpResourceGroup.String(): +// details = append(details, setGcpProps(propsString)...) +// case api.LwAccountResourceGroup.String(): +// details = append(details, setLwAccountProps(propsString)) +// case api.MachineResourceGroup.String(): +// details = append(details, setMachineProps(propsString)) +// } +// +// return details +//} func setBaseProps(props api.ResourceGroupProps) [][]string { var ( @@ -397,15 +309,14 @@ func IsDefault(isDefault int) string { } type resourceGroup struct { - Id string `json:"resource_guid"` - ResType string `json:"type"` - Name string `json:"name"` - Props api.ResourceGroupProps `json:"props"` - Enabled int `json:"enabled"` - IsDefault int `json:"isDefault"` - Query *api.RGQuery `json:"query"` - Description string `json:"description,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` - status string + Id string `json:"resource_guid"` + ResType string `json:"type"` + Name string `json:"name"` + Props api.ResourceGroupProps `json:"props"` + Enabled int `json:"enabled"` + IsDefaultBoolean bool `json:"isDefault"` + Query *api.RGQuery `json:"query"` + Description string `json:"description,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` + UpdatedBy string `json:"updatedBy,omitempty"` } From 31c9c8f6513ac4157e34a77769698cd5e847a6da Mon Sep 17 00:00:00 2001 From: Lei Jin Date: Tue, 29 Oct 2024 22:41:29 +0000 Subject: [PATCH 02/17] fix: Fix the gosimple lint errors Signed-off-by: Lei Jin --- api/resource_groups.go | 48 +++---- cli/cmd/resource_groups.go | 75 +++++------ cli/docs/lacework_cloud-account_migrate.md | 41 ++++++ cli/docs/lacework_compliance_aws_scan.md | 45 +++++++ cli/docs/lacework_compliance_azure_scan.md | 45 +++++++ cli/docs/lacework_compliance_google_scan.md | 45 +++++++ ...generate_cloud-account_aws_controltower.md | 117 ++++++++++++++++++ .../lacework_generate_cloud-account_oci.md | 69 +++++++++++ 8 files changed, 418 insertions(+), 67 deletions(-) create mode 100644 cli/docs/lacework_cloud-account_migrate.md create mode 100644 cli/docs/lacework_compliance_aws_scan.md create mode 100644 cli/docs/lacework_compliance_azure_scan.md create mode 100644 cli/docs/lacework_compliance_google_scan.md create mode 100644 cli/docs/lacework_generate_cloud-account_aws_controltower.md create mode 100644 cli/docs/lacework_generate_cloud-account_oci.md diff --git a/api/resource_groups.go b/api/resource_groups.go index 9f0561a5a..423f1dba5 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -93,13 +93,13 @@ type resourceGroupContext struct { // ResourceGroupTypes is the list of available Resource Group types var ResourceGroupTypes = map[resourceGroupType]resourceGroupContext{ - NoneResourceGroup: {resourceGroupType: "None", queryTemplate: NoneResourceGroupQueryTemplate}, - AwsResourceGroup: {resourceGroupType: "AWS", queryTemplate: AwsResourceGroupQueryTemplate}, - AzureResourceGroup: {resourceGroupType: "AZURE", queryTemplate: AzureResourceGroupQueryTemplate}, - ContainerResourceGroup: {resourceGroupType: "CONTAINER", queryTemplate: ContainerResourceGroupQueryTemplate}, - GcpResourceGroup: {resourceGroupType: "GCP", queryTemplate: GcpResourceGroupQueryTemplate}, - MachineResourceGroup: {resourceGroupType: "MACHINE", queryTemplate: MachineResourceGroupQueryTemplate}, - OciResourceGroup: {resourceGroupType: "OCI", queryTemplate: OciResourceGroupQueryTemplate}, + NoneResourceGroup: {resourceGroupType: "None", queryTemplate: NoneResourceGroupQueryTemplate}, + AwsResourceGroup: {resourceGroupType: "AWS", queryTemplate: AwsResourceGroupQueryTemplate}, + AzureResourceGroup: {resourceGroupType: "AZURE", queryTemplate: AzureResourceGroupQueryTemplate}, + ContainerResourceGroup: {resourceGroupType: "CONTAINER", queryTemplate: ContainerResourceGroupQueryTemplate}, + GcpResourceGroup: {resourceGroupType: "GCP", queryTemplate: GcpResourceGroupQueryTemplate}, + MachineResourceGroup: {resourceGroupType: "MACHINE", queryTemplate: MachineResourceGroupQueryTemplate}, + OciResourceGroup: {resourceGroupType: "OCI", queryTemplate: OciResourceGroupQueryTemplate}, KubernetesResourceGroup: {resourceGroupType: "KUBERNETES", queryTemplate: KubernetesResourceGroupQueryTemplate}, } @@ -182,12 +182,12 @@ func (svc *ResourceGroupsService) Update(data ResourceGroup) ( func castResourceGroupResponse(data resourceGroupWorkaroundData, response interface{}) error { group := ResourceGroupResponse{ Data: ResourceGroupData{ - IsDefaultBoolean: data.IsDefaultBoolean, + IsDefaultBoolean: data.IsDefaultBoolean, ResourceGroupGuid: data.ResourceGroupGuid, - Name: data.Name, - Type: data.Type, - Enabled: data.Enabled, - Query: data.Query, + Name: data.Name, + Type: data.Type, + Enabled: data.Enabled, + Query: data.Query, }, } @@ -219,19 +219,7 @@ func setResourceGroupsResponse(workaround resourceGroupsWorkaroundResponse) (Res func setResourceGroupResponse(response resourceGroupWorkaroundData) (ResourceGroupResponse, error) { return ResourceGroupResponse{ - Data: ResourceGroupData{ - Type: response.Type, - Enabled: response.Enabled, - Name: response.Name, - Query: response.Query, - Description: response.Description, - ResourceGroupGuid: response.ResourceGroupGuid, - CreatedTime: response.CreatedTime, - CreatedBy: response.CreatedBy, - UpdatedTime: response.UpdatedTime, - UpdatedBy: response.UpdatedBy, - IsDefaultBoolean: response.IsDefaultBoolean, - }, + Data: ResourceGroupData(response), }, nil } @@ -331,8 +319,8 @@ type ResourceGroupData struct { UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - Type string `json:"resourceType"` - Enabled int `json:"enabled"` + Type string `json:"resourceType"` + Enabled int `json:"enabled"` } // RAIN-21510 workaround @@ -345,7 +333,7 @@ type resourceGroupsWorkaroundResponse struct { } type resourceGroupWorkaroundData struct { - Name string `json:"name,omitempty"` + Name string `json:"name,omitempty"` Query *RGQuery `json:"query,omitempty"` Description string `json:"description,omitempty"` ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` @@ -354,6 +342,6 @@ type resourceGroupWorkaroundData struct { UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` + Type string `json:"resourceType"` + Enabled int `json:"enabled,omitempty"` } diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index 630495522..b10955887 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -19,13 +19,13 @@ package cmd import ( - "fmt" + "fmt" "strconv" -"time" + "time" "github.com/AlecAivazis/survey/v2" "github.com/lacework/go-sdk/api" - "github.com/pkg/errors" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -71,12 +71,12 @@ Then navigate to Settings > Resource Groups. for _, g := range resourceGroups.Data { groups = append(groups, resourceGroup{ - Id: g.ResourceGroupGuid, - ResType: g.Type, - Name: g.Name, - Enabled: g.Enabled, + Id: g.ResourceGroupGuid, + ResType: g.Type, + Name: g.Name, + Enabled: g.Enabled, IsDefaultBoolean: *g.IsDefaultBoolean, - Query: g.Query, + Query: g.Query, }) } @@ -111,15 +111,15 @@ Then navigate to Settings > Resource Groups. } group := resourceGroup{ - Id: response.Data.ResourceGroupGuid, - ResType: response.Data.Type, - Name: response.Data.Name, - Enabled: response.Data.Enabled, + Id: response.Data.ResourceGroupGuid, + ResType: response.Data.Type, + Name: response.Data.Name, + Enabled: response.Data.Enabled, IsDefaultBoolean: *response.Data.IsDefaultBoolean, - Query: response.Data.Query, - Description: response.Data.Description, - UpdatedBy: response.Data.UpdatedBy, - UpdatedTime: response.Data.UpdatedTime, + Query: response.Data.Query, + Description: response.Data.Description, + UpdatedBy: response.Data.UpdatedBy, + UpdatedTime: response.Data.UpdatedTime, } if cli.JSONOutput() { @@ -138,7 +138,7 @@ Then navigate to Settings > Resource Groups. ) cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP GUID", "TYPE", "NAME", "ENABLED", "IS_DEFAULT_BOOLEAN"}, - groupCommon)) + groupCommon)) cli.OutputHuman("\n") //cli.OutputHuman(buildResourceGroupPropsTable(group)) } else { @@ -191,6 +191,7 @@ Then navigate to Settings > Resource Groups. }, } ) + func promptCreateResourceGroup() error { resourceGroupOptions := []string{ @@ -279,16 +280,16 @@ func promptCreateResourceGroup() error { // return details //} -func setBaseProps(props api.ResourceGroupProps) [][]string { - var ( - details [][]string - ) - lastUpdated := props.GetBaseProps().LastUpdated - details = append(details, []string{"DESCRIPTION", props.GetBaseProps().Description}) - details = append(details, []string{"UPDATED BY", props.GetBaseProps().UpdatedBy}) - details = append(details, []string{"LAST UPDATED", lastUpdated.String()}) - return details -} +//func setBaseProps(props api.ResourceGroupProps) [][]string { +// var ( +// details [][]string +// ) +// lastUpdated := props.GetBaseProps().LastUpdated +// details = append(details, []string{"DESCRIPTION", props.GetBaseProps().Description}) +// details = append(details, []string{"UPDATED BY", props.GetBaseProps().UpdatedBy}) +// details = append(details, []string{"LAST UPDATED", lastUpdated.String()}) +// return details +//} func init() { // add the resource-group command @@ -309,14 +310,14 @@ func IsDefault(isDefault int) string { } type resourceGroup struct { - Id string `json:"resource_guid"` - ResType string `json:"type"` - Name string `json:"name"` - Props api.ResourceGroupProps `json:"props"` - Enabled int `json:"enabled"` - IsDefaultBoolean bool `json:"isDefault"` - Query *api.RGQuery `json:"query"` - Description string `json:"description,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` + Id string `json:"resource_guid"` + ResType string `json:"type"` + Name string `json:"name"` + Props api.ResourceGroupProps `json:"props"` + Enabled int `json:"enabled"` + IsDefaultBoolean bool `json:"isDefault"` + Query *api.RGQuery `json:"query"` + Description string `json:"description,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` + UpdatedBy string `json:"updatedBy,omitempty"` } diff --git a/cli/docs/lacework_cloud-account_migrate.md b/cli/docs/lacework_cloud-account_migrate.md new file mode 100644 index 000000000..a94e153f7 --- /dev/null +++ b/cli/docs/lacework_cloud-account_migrate.md @@ -0,0 +1,41 @@ +--- +title: "lacework cloud-account migrate" +slug: lacework_cloud-account_migrate +hide_title: true +--- + +## lacework cloud-account migrate + +Mark a GCPv1 (storage-based) cloud account integration for migration + +``` +lacework cloud-account migrate [flags] +``` + +### Options + +``` + -h, --help help for migrate +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) +``` + +### SEE ALSO + +* [lacework cloud-account](lacework_cloud-account.md) - Manage cloud accounts + diff --git a/cli/docs/lacework_compliance_aws_scan.md b/cli/docs/lacework_compliance_aws_scan.md new file mode 100644 index 000000000..68c497d65 --- /dev/null +++ b/cli/docs/lacework_compliance_aws_scan.md @@ -0,0 +1,45 @@ +--- +title: "lacework compliance aws scan" +slug: lacework_compliance_aws_scan +hide_title: true +--- + +## lacework compliance aws scan + +Scan triggers a new resource inventory scan + +### Synopsis + +Scan triggers a new resource inventory scan. + +``` +lacework compliance aws scan [flags] +``` + +### Options + +``` + -h, --help help for scan +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) +``` + +### SEE ALSO + +* [lacework compliance aws](lacework_compliance_aws.md) - Compliance for AWS + diff --git a/cli/docs/lacework_compliance_azure_scan.md b/cli/docs/lacework_compliance_azure_scan.md new file mode 100644 index 000000000..f71fd0d76 --- /dev/null +++ b/cli/docs/lacework_compliance_azure_scan.md @@ -0,0 +1,45 @@ +--- +title: "lacework compliance azure scan" +slug: lacework_compliance_azure_scan +hide_title: true +--- + +## lacework compliance azure scan + +Scan triggers a new resource inventory scan + +### Synopsis + +Scan triggers a new resource inventory scan. + +``` +lacework compliance azure scan [flags] +``` + +### Options + +``` + -h, --help help for scan +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) +``` + +### SEE ALSO + +* [lacework compliance azure](lacework_compliance_azure.md) - Compliance for Azure Cloud + diff --git a/cli/docs/lacework_compliance_google_scan.md b/cli/docs/lacework_compliance_google_scan.md new file mode 100644 index 000000000..a6c4b2d83 --- /dev/null +++ b/cli/docs/lacework_compliance_google_scan.md @@ -0,0 +1,45 @@ +--- +title: "lacework compliance google scan" +slug: lacework_compliance_google_scan +hide_title: true +--- + +## lacework compliance google scan + +Scan triggers a new resource inventory scan + +### Synopsis + +Scan triggers a new resource inventory scan. + +``` +lacework compliance google scan [flags] +``` + +### Options + +``` + -h, --help help for scan +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) +``` + +### SEE ALSO + +* [lacework compliance google](lacework_compliance_google.md) - Compliance for Google Cloud + diff --git a/cli/docs/lacework_generate_cloud-account_aws_controltower.md b/cli/docs/lacework_generate_cloud-account_aws_controltower.md new file mode 100644 index 000000000..260cf248d --- /dev/null +++ b/cli/docs/lacework_generate_cloud-account_aws_controltower.md @@ -0,0 +1,117 @@ +--- +title: "lacework generate cloud-account aws controltower" +slug: lacework_generate_cloud-account_aws_controltower +hide_title: true +--- + +## lacework generate cloud-account aws controltower + +Generate and/or execute Terraform code for ControlTower integration + +### Synopsis + +Use this command to generate Terraform code for deploying Lacework with Aws Cloudtrail and +ControlTower. + +By default, this command interactively prompts for the required information to set up the new cloud account. +In interactive mode, this command will: + +* Prompt for the required information to set up the integration +* Generate new Terraform code using the inputs +* Optionally, run the generated Terraform code: + * If Terraform is already installed, the version is verified as compatible for use + * If Terraform is not installed, or the version installed is not compatible, a new + version will be installed into a temporary location + * Once Terraform is detected or installed, the Terraform plan is executed + * The command prompts you with the outcome of the plan and allows you to view more + details or continue with Terraform apply + * If confirmed, Terraform apply runs, completing the setup of the cloud account + +This command can also be run in noninteractive mode. +See help output for more details on the parameter values required for Terraform code generation. + + +``` +lacework generate cloud-account aws controltower [flags] +``` + +### Options + +``` + --apply run terraform apply without executing plan or prompting + --audit_account string The audit account flag input in the format profile:region + -h, --help help for controltower + --iam_role_arn string specify the arn of the existing iam role + --iam_role_external_id string specify the external id of the existing iam role + --iam_role_name string specify the name of the existing iam role + --lacework_aws_account_id string the Lacework AWS root account id + --log_archive_account string The log archive account flag input in the format profile:region + --org_account_mapping string Org account mapping json string. Example: '{"default_lacework_account":"main", "mapping": [{ "aws_accounts": ["123456789011"], "lacework_account": "sub-account-1"}]}' + --output string location to write generated content + --prefix string specify the prefix that will be used at the beginning of every generated resource + --s3_bucket_arn string the S3 Bucket for consolidated CloudTrail + --sns_topic_arn string the SNS Topic + --sqs_queue_name string specify the name of the sqs queue +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + --agentless enable agentless integration + --agentless_management_account_id string AWS management account ID for Agentless integration + --agentless_monitored_account_ids strings AWS monitored account IDs for Agentless integrations; may contain account IDs, OUs, or the organization root (e.g. 123456789000,ou-abcd-12345678,r-abcd) + --agentless_monitored_accounts strings AWS monitored accounts for Agentless integrations; value format must be : + --agentless_scanning_accounts strings AWS scanning accounts for Agentless integrations; value format must be : + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --aws_assume_role string specify aws assume role + --aws_organization enable organization integration + --aws_profile string specify aws profile + --aws_region string specify aws region + --aws_subaccount strings configure an additional aws account; value format must be : + --bucket_encryption_enabled enable S3 bucket encryption when creating bucket (default true) + --bucket_name string specify bucket name when creating bucket + --bucket_sse_key_arn string specify existing KMS encryption key arn for bucket + --cloudtrail enable cloudtrail integration + --cloudtrail_name string specify name of cloudtrail integration + --cloudtrail_org_account_mapping string Org account mapping json string. Example: '{"default_lacework_account":"main", "mapping": [{ "aws_accounts": ["123456789011"], "lacework_account": "sub-account-1"}]}' + --config enable config integration + --config_cf_resource_prefix string specify Cloudformation resource prefix for Config organization integration + --config_lacework_access_key_id string specify AWS access key ID for Config organization integration + --config_lacework_account string specify lacework account for Config organization integration + --config_lacework_secret_key string specify AWS secret key for Config organization integration + --config_lacework_sub_account string specify lacework sub-account for Config organization integration + --config_organization_id string specify AWS organization ID for Config organization integration + --config_organization_units strings specify AWS organization units for Config organization integration + --consolidated_cloudtrail use consolidated trail + --controltower enable Control Tower integration + --controltower_audit_account string specify AWS Control Tower Audit account; value format must be : + --controltower_kms_key_arn string specify AWS Control Tower custom kMS key ARN + --controltower_log_archive_account string specify AWS Control Tower Log Archive account; value format must be : + --debug turn on debug logging + --existing_bucket_arn string specify existing cloudtrail S3 bucket ARN + --existing_iam_role_arn string specify existing iam role arn to use + --existing_iam_role_externalid string specify existing iam role external_id to use + --existing_iam_role_name string specify existing iam role name to use + --existing_sns_topic_arn string specify existing SNS topic arn + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --sns_topic_encryption_enabled enable encryption on SNS topic when creating one (default true) + --sns_topic_encryption_key_arn string specify existing KMS encryption key arn for SNS topic + --sns_topic_name string specify SNS topic name if creating new one + --sqs_encryption_enabled enable encryption on SQS queue when creating (default true) + --sqs_encryption_key_arn string specify existing KMS encryption key arn for SQS queue + --subaccount string sub-account name inside your organization (org admins only) + --use_s3_bucket_notification enable S3 bucket notifications +``` + +### SEE ALSO + +* [lacework generate cloud-account aws](lacework_generate_cloud-account_aws.md) - Generate and/or execute Terraform code for AWS integration + diff --git a/cli/docs/lacework_generate_cloud-account_oci.md b/cli/docs/lacework_generate_cloud-account_oci.md new file mode 100644 index 000000000..5ddc3e20d --- /dev/null +++ b/cli/docs/lacework_generate_cloud-account_oci.md @@ -0,0 +1,69 @@ +--- +title: "lacework generate cloud-account oci" +slug: lacework_generate_cloud-account_oci +hide_title: true +--- + +## lacework generate cloud-account oci + +Generate and/or execute Terraform code for OCI integration + +### Synopsis + +Use this command to generate Terraform code for deploying Lacework into an OCI tenant. + +By default, this command interactively prompts for the required information to setup the new cloud account. +In interactive mode, this command will: + +* Prompt for the required information to setup the integration +* Generate new Terraform code using the inputs +* Optionally, run the generated Terraform code: + * If Terraform is already installed, the version is verified as compatible for use + * If Terraform is not installed, or the version installed is not compatible, a new + version will be installed into a temporary location + * Once Terraform is detected or installed, Terraform plan will be executed + * The command will prompt with the outcome of the plan and allow to view more details + or continue with Terraform apply + * If confirmed, Terraform apply will be run, completing the setup of the cloud account + +This command can also be run in noninteractive mode. +See help output for more details on the parameter value(s) required for Terraform code generation. + + +``` +lacework generate cloud-account oci [flags] +``` + +### Options + +``` + --apply run terraform apply without executing plan or prompting + --config enable configuration integration + --config_name string specify name of configuration integration + -h, --help help for oci + --oci_user_email string specify the email address to associate with the integration OCI user + --output string location to write generated content (default is ~/lacework/oci) + --tenant_ocid string specify the OCID of the tenant to integrate +``` + +### Options inherited from parent commands + +``` + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) +``` + +### SEE ALSO + +* [lacework generate cloud-account](lacework_generate_cloud-account.md) - Generate cloud integration IaC + From 546c09727a92487434681f1ca5a499e3432c8446 Mon Sep 17 00:00:00 2001 From: Lei Jin Date: Tue, 29 Oct 2024 22:47:08 +0000 Subject: [PATCH 03/17] fix: Fix the build issue Signed-off-by: Lei Jin --- integration/resource_groups_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/resource_groups_test.go b/integration/resource_groups_test.go index ca8e40193..2f7c1fe03 100644 --- a/integration/resource_groups_test.go +++ b/integration/resource_groups_test.go @@ -167,7 +167,7 @@ func TestResourceGroupDelete(t *testing.T) { // test each RGv2 type against its default template for i := range api.ResourceGroupTypes { switch i { - case api.NoneResourceGroup, api.LwAccountResourceGroup: + case api.NoneResourceGroup: // these resource groups are not applicable continue default: From 07bbc9023f94c80e241e2fede2f15344c2d4fded Mon Sep 17 00:00:00 2001 From: Lei Jin Date: Tue, 29 Oct 2024 23:55:58 +0000 Subject: [PATCH 04/17] fix: Fix unit tests Signed-off-by: Lei Jin --- api/resource_groups_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/api/resource_groups_test.go b/api/resource_groups_test.go index 93a87eb78..f3ceaf914 100644 --- a/api/resource_groups_test.go +++ b/api/resource_groups_test.go @@ -38,7 +38,6 @@ func TestResourceGroupTypes(t *testing.T) { assert.Equal(t, "CONTAINER", api.ContainerResourceGroup.String(), "wrong resource group type") assert.Equal(t, "GCP", api.GcpResourceGroup.String(), "wrong resource group type") assert.Equal(t, "MACHINE", api.MachineResourceGroup.String(), "wrong resource group type") - assert.Equal(t, "LW_ACCOUNT", api.LwAccountResourceGroup.String(), "wrong resource group type") } func TestFindResourceGroupType(t *testing.T) { @@ -67,9 +66,9 @@ func TestFindResourceGroupType(t *testing.T) { assert.True(t, found, "resource group type should exist") assert.Equal(t, "MACHINE", groupFound.String(), "wrong resource group type") - groupFound, found = api.FindResourceGroupType("LW_ACCOUNT") + groupFound, found = api.FindResourceGroupType("KUBERNETES") assert.True(t, found, "resource group type should exist") - assert.Equal(t, "LW_ACCOUNT", groupFound.String(), "wrong resource group type") + assert.Equal(t, "KUBERNETES", groupFound.String(), "wrong resource group type") } func TestResourceGroupGet(t *testing.T) { @@ -612,7 +611,6 @@ func singleVanillaResourceGroup(id string, iType string, query string) string { } return ` { - "guid": "` + id + `", "isDefaultBoolean": "true", "query": ` + query + `, "resourceGroupGuid": "` + id + `", From 5679383d3c7946f6c1deb225d0a5a4ef783defda Mon Sep 17 00:00:00 2001 From: Lei Jin Date: Wed, 30 Oct 2024 00:38:47 +0000 Subject: [PATCH 05/17] fix: Fix the rgv2 list bug Signed-off-by: Lei Jin --- api/resource_groups_test.go | 34 +++++++++++++++++----------------- api/resource_groups_v2.go | 5 ++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/api/resource_groups_test.go b/api/resource_groups_test.go index f3ceaf914..1ad84f6a8 100644 --- a/api/resource_groups_test.go +++ b/api/resource_groups_test.go @@ -267,13 +267,13 @@ func singleAwsResourceGroup(id string) string { return ` { "guid": "` + id + `", - "description": "All Aws Resources" + "description": "All Aws Resources", "isDefaultBoolean": true, "resourceGroupGuid": "` + id + `", "resourceName": "group_name", "resourceType": "AWS", "enabled": 1, - "query": "{ + "query": { "filters": { "filter1": { "field": "AWS_ACCOUNT_ID", @@ -322,7 +322,7 @@ func singleAwsResourceGroup(id string) string { } ] } - }" + } } ` } @@ -332,13 +332,13 @@ func singleAzureResourceGroup(id string) string { return ` { "guid": "` + id + `", - "description": "All Azure Resources" + "description": "All Azure Resources", "isDefaultBoolean": true, "resourceGroupGuid": "` + id + `", "resourceName": "group_name", "resourceType": "AZURE", "enabled": 1, - "query": "{ + "query": { "filters": { "filter1": { "field": "AZURE_TENANT_ID", @@ -407,7 +407,7 @@ func singleAzureResourceGroup(id string) string { } ] } - }" + } } ` } @@ -416,13 +416,13 @@ func singleContainerResourceGroup(id string) string { return ` { "guid": "` + id + `", - "description": "All Container Resources" + "description": "All Container Resources", "isDefaultBoolean": true, "resourceGroupGuid": "` + id + `", "resourceName": "group_name", "resourceType": "CONTAINER", "enabled": 1, - "query": "{ + "query": { "filters": { "filter1": { "field": "IMAGE_TAG", @@ -471,7 +471,7 @@ func singleContainerResourceGroup(id string) string { } ] } - }" + } } ` } @@ -480,13 +480,13 @@ func singleGcpResourceGroup(id string) string { return ` { "guid": "` + id + `", - "description": "All GCP Resources" + "description": "All GCP Resources", "isDefaultBoolean": true, "resourceGroupGuid": "` + id + `", "resourceName": "group_name", "resourceType": "GCP", "enabled": 1, - "query": "{ + "query": { "filters": { "filter1": { "field": "GCP_ORGANIZATION_ID", @@ -555,7 +555,7 @@ func singleGcpResourceGroup(id string) string { } ] } - }" + } } ` } @@ -564,13 +564,13 @@ func singleMachineResourceGroup(id string) string { return ` { "guid": "` + id + `", - "description": "All Machine Resources" + "description": "All Machine Resources", "isDefaultBoolean": true, "resourceGroupGuid": "` + id + `", "resourceName": "group_name", "resourceType": "MACHINE", "enabled": 1, - "query": "{ + "query": { "filters": { "filter1": { "field": "MACHINE_TAGS", @@ -584,7 +584,7 @@ func singleMachineResourceGroup(id string) string { "expression": { "filterName": "filter1" } - }" + } } ` } @@ -611,12 +611,12 @@ func singleVanillaResourceGroup(id string, iType string, query string) string { } return ` { - "isDefaultBoolean": "true", + "isDefaultBoolean": true, "query": ` + query + `, "resourceGroupGuid": "` + id + `", "name": "group_name", "resourceType": "` + iType + `", - "enabled": 1, + "enabled": 1 } ` } diff --git a/api/resource_groups_v2.go b/api/resource_groups_v2.go index 5b415d756..26899ad1d 100644 --- a/api/resource_groups_v2.go +++ b/api/resource_groups_v2.go @@ -29,10 +29,10 @@ func (svc *ResourceGroupsV2Service) List() (response ResourceGroupsV2Response, e var rawResponse ResourceGroupsV2Response err = svc.client.RequestDecoder("GET", apiV2ResourceGroups, nil, &rawResponse) if err != nil { - return + return rawResponse, err } - return + return rawResponse, nil } func (svc *ResourceGroupsV2Service) Create(group ResourceGroupDataWithQuery) ( @@ -172,7 +172,6 @@ func (group ResourceGroupDataWithQuery) ID() string { return group.ResourceGroupGuid } - func (group *ResourceGroupDataWithQuery) ResetResourceGUID() { group.ResourceGroupGuid = "" } From cf698f77cdc496eabb76ee1aac672722ce564d63 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 07:08:31 -0500 Subject: [PATCH 06/17] fix: Add Kubernetes support --- .../resource_groups/kubernetes.json | 49 +++ api/resource_groups_test.go | 349 ++++++++++++------ 2 files changed, 294 insertions(+), 104 deletions(-) diff --git a/api/_templates/resource_groups/kubernetes.json b/api/_templates/resource_groups/kubernetes.json index e69de29bb..a91d047c0 100644 --- a/api/_templates/resource_groups/kubernetes.json +++ b/api/_templates/resource_groups/kubernetes.json @@ -0,0 +1,49 @@ +{ + "filters": { + "filter1": { + "field": "AWS Account", + "operation": "EQUALS", + "values": [ + "123456789012" + ] + }, + "filter2": { + "field": "AWS Region", + "operation": "EQUALS", + "values": [ + "us-west-2" + ] + }, + "filter3": { + "field": "Cluster Name", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter4": { + "field": "Namespace", + "operation": "EQUALS", + "values": [ + "prod" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + } + ] + } +} \ No newline at end of file diff --git a/api/resource_groups_test.go b/api/resource_groups_test.go index 1ad84f6a8..f6cb1ab81 100644 --- a/api/resource_groups_test.go +++ b/api/resource_groups_test.go @@ -38,6 +38,8 @@ func TestResourceGroupTypes(t *testing.T) { assert.Equal(t, "CONTAINER", api.ContainerResourceGroup.String(), "wrong resource group type") assert.Equal(t, "GCP", api.GcpResourceGroup.String(), "wrong resource group type") assert.Equal(t, "MACHINE", api.MachineResourceGroup.String(), "wrong resource group type") + assert.Equal(t, "OCI", api.OciResourceGroup.String(), "wrong resource group type") + assert.Equal(t, "KUBERNETES", api.KubernetesResourceGroup.String(), "wrong resource group type") } func TestFindResourceGroupType(t *testing.T) { @@ -194,12 +196,15 @@ func TestResourceGroupsDelete(t *testing.T) { func TestResourceGroupsList(t *testing.T) { var ( - awsResourceGUIDs = []string{intgguid.New(), intgguid.New()} - azureResourceGUIDs = []string{intgguid.New(), intgguid.New()} - containerResourceGUIDs = []string{intgguid.New()} - gcpResourceGUIDs = []string{intgguid.New()} - machineResourceGUIDs = []string{intgguid.New()} - allGroups = [][]string{awsResourceGUIDs, azureResourceGUIDs, containerResourceGUIDs, gcpResourceGUIDs, machineResourceGUIDs} + awsResourceGUIDs = []string{intgguid.New(), intgguid.New()} + azureResourceGUIDs = []string{intgguid.New(), intgguid.New()} + containerResourceGUIDs = []string{intgguid.New()} + gcpResourceGUIDs = []string{intgguid.New()} + machineResourceGUIDs = []string{intgguid.New()} + ociResourceGUIDs = []string{intgguid.New()} + kubernetesResourceGUIDs = []string{intgguid.New()} + allGroups = [][]string{awsResourceGUIDs, azureResourceGUIDs, containerResourceGUIDs, + gcpResourceGUIDs, machineResourceGUIDs, ociResourceGUIDs, kubernetesResourceGUIDs} allGuids []string fakeServer = lacework.MockServer() ) @@ -219,6 +224,8 @@ func TestResourceGroupsList(t *testing.T) { generateResourceGroups(containerResourceGUIDs, "CONTAINER"), generateResourceGroups(gcpResourceGUIDs, "GCP"), generateResourceGroups(machineResourceGUIDs, "MACHINE"), + generateResourceGroups(ociResourceGUIDs, "OCI"), + generateResourceGroups(kubernetesResourceGUIDs, "KUBERNETES"), } fmt.Fprintf(w, generateResourceGroupsResponse( @@ -258,6 +265,11 @@ func generateResourceGroups(guids []string, iType string) string { resourceGroups[i] = singleGcpResourceGroup(guid) case api.MachineResourceGroup.String(): resourceGroups[i] = singleMachineResourceGroup(guid) + case api.OciResourceGroup.String(): + resourceGroups[i] = singleOciResourceGroup(guid) + case api.KubernetesResourceGroup.String(): + resourceGroups[i] = singleKubernetesResourceGroup(guid) + } } return strings.Join(resourceGroups, ", ") @@ -276,21 +288,21 @@ func singleAwsResourceGroup(id string) string { "query": { "filters": { "filter1": { - "field": "AWS_ACCOUNT_ID", + "field": "Account", "operation": "EQUALS", "values": [ "*" ] }, "filter2": { - "field": "AWS_ORGANIZATION_ID", + "field": "Organization ID", "operation": "EQUALS", "values": [ "*" ] }, "filter3": { - "field": "AWS_RESOURCE_TAGS", + "field": "Resource Tag", "operation": "EQUALS", "key": "*", "values": [ @@ -298,7 +310,7 @@ func singleAwsResourceGroup(id string) string { ] }, "filter4": { - "field": "AWS_RESOURCE_REGION", + "field": "Region", "operation": "EQUALS", "values": [ "*" @@ -341,21 +353,21 @@ func singleAzureResourceGroup(id string) string { "query": { "filters": { "filter1": { - "field": "AZURE_TENANT_ID", + "field": "Tenant ID", "operation": "EQUALS", "values": [ "*" ] }, "filter2": { - "field": "AZURE_SUBSCRIPTION_ID", + "field": "Subscription ID", "operation": "EQUALS", "values": [ "*" ] }, "filter3": { - "field": "AZURE_RESOURCE_TAGS", + "field": "Resource Tag", "operation": "EQUALS", "key": "*", "values": [ @@ -363,21 +375,21 @@ func singleAzureResourceGroup(id string) string { ] }, "filter4": { - "field": "AZURE_RESOURCE_REGION", + "field": "Region", "operation": "EQUALS", "values": [ "*" ] }, "filter5": { - "field": "AZURE_TENANT_NAME", + "field": "Tenant Name", "operation": "EQUALS", "values": [ "*" ] }, "filter6": { - "field": "AZURE_SUBSCRIPTION_NAME", + "field": "Subscription Name", "operation": "EQUALS", "values": [ "*" @@ -425,14 +437,15 @@ func singleContainerResourceGroup(id string) string { "query": { "filters": { "filter1": { - "field": "IMAGE_TAG", + "field": "Container Tag", "operation": "EQUALS", "values": [ "*" - ] + ], + "key": "*" }, "filter2": { - "field": "CONTAINER_LABELS", + "field": "Container Label", "operation": "EQUALS", "values": [ "*" @@ -440,14 +453,14 @@ func singleContainerResourceGroup(id string) string { "key": "*" }, "filter3": { - "field": "IMAGE_REPO", + "field": "Image Repo", "operation": "EQUALS", "values": [ "*" ] }, "filter4": { - "field": "IMAGE_REGISTRY", + "field": "Image Registry", "operation": "EQUALS", "values": [ "*" @@ -487,75 +500,75 @@ func singleGcpResourceGroup(id string) string { "resourceType": "GCP", "enabled": 1, "query": { - "filters": { - "filter1": { - "field": "GCP_ORGANIZATION_ID", - "operation": "EQUALS", - "values": [ - "*" - ] - }, - "filter2": { - "field": "GCP_FOLDER_IDS", - "operation": "EQUALS", - "values": [ - "*" - ] - }, - "filter3": { - "field": "GCP_PROJECT_ID", - "operation": "EQUALS", - "values": [ - "*" - ] - }, - "filter4": { - "field": "GCP_RESOURCE_TAGS", - "operation": "EQUALS", - "key": "*", - "values": [ - "*" - ] - }, - "filter5": { - "field": "GCP_RESOURCE_REGION", - "operation": "EQUALS", - "values": [ - "*" - ] - }, - "filter6": { - "field": "GCP_ORGANIZATION_NAME", - "operation": "EQUALS", - "values": [ - "*" - ] - } - }, - "expression": { - "operator": "OR", - "children": [ - { - "filterName": "filter1" - }, - { - "filterName": "filter2" - }, - { - "filterName": "filter3" - }, - { - "filterName": "filter4" - }, - { - "filterName": "filter5" - }, - { - "filterName": "filter5" - } - ] - } - } + "filters": { + "filter1": { + "field": "Organization ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "Folder", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "Project ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter4": { + "field": "Resource Label", + "operation": "EQUALS", + "key": "*", + "values": [ + "*" + ] + }, + "filter5": { + "field": "Region", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter6": { + "field": "Organization Name", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + }, + { + "filterName": "filter5" + }, + { + "filterName": "filter5" + } + ] + } + } } ` } @@ -571,20 +584,148 @@ func singleMachineResourceGroup(id string) string { "resourceType": "MACHINE", "enabled": 1, "query": { - "filters": { - "filter1": { - "field": "MACHINE_TAGS", - "operation": "EQUALS", - "values": [ - "*" - ], - "key": "*" - } - }, - "expression": { - "filterName": "filter1" - } - } + "filters": { + "filter1": { + "field": "Machine Tag", + "operation": "EQUALS", + "values": [ + "*" + ], + "key": "*" + } + }, + "expression": { + "filterName": "filter1" + } + } + } + ` +} + +func singleOciResourceGroup(id string) string { + return ` + { + "guid": "` + id + `", + "description": "All OCI Resources", + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "OCI", + "enabled": 1, + "query": { + "filters": { + "filter1": { + "field": "Compartment ID", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "Compartment Name", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "Resource Tag", + "operation": "EQUALS", + "key": "*", + "values": [ + "*" + ] + }, + "filter4": { + "field": "Region", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + } + ] + } + } + } + ` +} + +func singleKubernetesResourceGroup(id string) string { + + return ` + { + "guid": "` + id + `", + "description": "All Kubernetes Resources", + "isDefaultBoolean": true, + "resourceGroupGuid": "` + id + `", + "resourceName": "group_name", + "resourceType": "KUBERNETES", + "enabled": 1, + "query": { + "filters": { + "filter1": { + "field": "AWS Account", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter2": { + "field": "AWS Region", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter3": { + "field": "Cluster Name", + "operation": "EQUALS", + "values": [ + "*" + ] + }, + "filter4": { + "field": "Namespace", + "operation": "EQUALS", + "values": [ + "*" + ] + } + }, + "expression": { + "operator": "OR", + "children": [ + { + "filterName": "filter1" + }, + { + "filterName": "filter2" + }, + { + "filterName": "filter3" + }, + { + "filterName": "filter4" + } + ] + } + } } ` } From ceceb23b228bebb9b991080d3afc9e86dffb4779 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 07:18:53 -0500 Subject: [PATCH 07/17] fix: rg api tests --- api/resource_groups_test.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/api/resource_groups_test.go b/api/resource_groups_test.go index f6cb1ab81..8b38dbee5 100644 --- a/api/resource_groups_test.go +++ b/api/resource_groups_test.go @@ -52,22 +52,26 @@ func TestFindResourceGroupType(t *testing.T) { assert.True(t, found, "resource group type should exist") assert.Equal(t, "AWS", groupFound.String(), "wrong resource group type") - groupFound, found = api.FindResourceGroupType("AWS") + groupFound, found = api.FindResourceGroupType("GCP") assert.True(t, found, "resource group type should exist") - assert.Equal(t, "AWS", groupFound.String(), "wrong resource group type") + assert.Equal(t, "GCP", groupFound.String(), "wrong resource group type") - groupFound, found = api.FindResourceGroupType("CONTAINER") + groupFound, found = api.FindResourceGroupType("AZURE") assert.True(t, found, "resource group type should exist") - assert.Equal(t, "CONTAINER", groupFound.String(), "wrong resource group type") + assert.Equal(t, "AZURE", groupFound.String(), "wrong resource group type") - groupFound, found = api.FindResourceGroupType("GCP") + groupFound, found = api.FindResourceGroupType("CONTAINER") assert.True(t, found, "resource group type should exist") - assert.Equal(t, "GCP", groupFound.String(), "wrong resource group type") + assert.Equal(t, "CONTAINER", groupFound.String(), "wrong resource group type") groupFound, found = api.FindResourceGroupType("MACHINE") assert.True(t, found, "resource group type should exist") assert.Equal(t, "MACHINE", groupFound.String(), "wrong resource group type") + groupFound, found = api.FindResourceGroupType("OCI") + assert.True(t, found, "resource group type should exist") + assert.Equal(t, "OCI", groupFound.String(), "wrong resource group type") + groupFound, found = api.FindResourceGroupType("KUBERNETES") assert.True(t, found, "resource group type should exist") assert.Equal(t, "KUBERNETES", groupFound.String(), "wrong resource group type") @@ -196,17 +200,17 @@ func TestResourceGroupsDelete(t *testing.T) { func TestResourceGroupsList(t *testing.T) { var ( - awsResourceGUIDs = []string{intgguid.New(), intgguid.New()} - azureResourceGUIDs = []string{intgguid.New(), intgguid.New()} - containerResourceGUIDs = []string{intgguid.New()} - gcpResourceGUIDs = []string{intgguid.New()} - machineResourceGUIDs = []string{intgguid.New()} - ociResourceGUIDs = []string{intgguid.New()} + awsResourceGUIDs = []string{intgguid.New(), intgguid.New()} + azureResourceGUIDs = []string{intgguid.New(), intgguid.New()} + containerResourceGUIDs = []string{intgguid.New()} + gcpResourceGUIDs = []string{intgguid.New()} + machineResourceGUIDs = []string{intgguid.New()} + ociResourceGUIDs = []string{intgguid.New()} kubernetesResourceGUIDs = []string{intgguid.New()} - allGroups = [][]string{awsResourceGUIDs, azureResourceGUIDs, containerResourceGUIDs, + allGroups = [][]string{awsResourceGUIDs, azureResourceGUIDs, containerResourceGUIDs, gcpResourceGUIDs, machineResourceGUIDs, ociResourceGUIDs, kubernetesResourceGUIDs} - allGuids []string - fakeServer = lacework.MockServer() + allGuids []string + fakeServer = lacework.MockServer() ) for _, guids := range allGroups { From 561393f851c3b2b21e5a9964f4832f6e94db28ed Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 07:48:27 -0500 Subject: [PATCH 08/17] fix: Remove ResourceGroupProps references --- api/resource_groups.go | 11 ---------- api/resource_groups_v2.go | 17 --------------- cli/cmd/resource_groups.go | 43 +++++++++++++------------------------- 3 files changed, 15 insertions(+), 56 deletions(-) diff --git a/api/resource_groups.go b/api/resource_groups.go index 423f1dba5..403fa01ba 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -24,7 +24,6 @@ import ( "fmt" "time" - "github.com/lacework/go-sdk/lwtime" "github.com/pkg/errors" ) @@ -34,16 +33,6 @@ type ResourceGroupsService struct { client *Client } -type ResourceGroupProps interface { - GetBaseProps() ResourceGroupPropsBase -} - -type ResourceGroupPropsBase struct { - Description string `json:"description"` - UpdatedBy string `json:"updatedBy,omitempty"` - LastUpdated *lwtime.Epoch `json:"lastUpdated,omitempty"` -} - type ResourceGroup interface { ID() string ResourceGroupType() resourceGroupType diff --git a/api/resource_groups_v2.go b/api/resource_groups_v2.go index 26899ad1d..04843917e 100644 --- a/api/resource_groups_v2.go +++ b/api/resource_groups_v2.go @@ -155,27 +155,10 @@ type ResourceGroupDataWithQuery struct { IsOrg *bool `json:"isOrg,omitempty"` } -func (group ResourceGroupDataWithQuery) GetProps() interface{} { - return nil -} - -func (group ResourceGroupDataWithQuery) GetQuery() *RGQuery { - return group.Query -} - -func (group ResourceGroupDataWithQuery) ResourceGroupType() resourceGroupType { - t, _ := FindResourceGroupType(group.Type) - return t -} - func (group ResourceGroupDataWithQuery) ID() string { return group.ResourceGroupGuid } -func (group *ResourceGroupDataWithQuery) ResetResourceGUID() { - group.ResourceGroupGuid = "" -} - func (group ResourceGroupDataWithQuery) IsV2Group() bool { return true } diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index b10955887..e351d83cc 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -131,24 +131,12 @@ Then navigate to Settings > Resource Groups. var groupCommon [][]string - if group.Props != nil { - groupCommon = append(groupCommon, - []string{group.Id, group.ResType, group.Name, strconv.Itoa(group.Enabled), - strconv.FormatBool(group.IsDefaultBoolean)}, - ) - cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP GUID", "TYPE", "NAME", "ENABLED", - "IS_DEFAULT_BOOLEAN"}, - groupCommon)) - cli.OutputHuman("\n") - //cli.OutputHuman(buildResourceGroupPropsTable(group)) - } else { - groupCommon = append(groupCommon, - []string{group.Id, group.ResType, group.Name, group.Description, strconv.Itoa(group.Enabled), - strconv.FormatBool(group.IsDefaultBoolean), group.UpdatedBy, group.UpdatedTime.UTC().String()}, - ) - cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "DESCRIPTION", "STATE", - "DEFAULT", "UPDATED BY", "UPDATED_TIME"}, groupCommon)) - } + groupCommon = append(groupCommon, + []string{group.Id, group.ResType, group.Name, group.Description, strconv.Itoa(group.Enabled), + strconv.FormatBool(group.IsDefaultBoolean), group.UpdatedBy, group.UpdatedTime.UTC().String()}, + ) + cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "DESCRIPTION", "STATE", + "DEFAULT", "UPDATED BY", "UPDATED_TIME"}, groupCommon)) return nil }, @@ -310,14 +298,13 @@ func IsDefault(isDefault int) string { } type resourceGroup struct { - Id string `json:"resource_guid"` - ResType string `json:"type"` - Name string `json:"name"` - Props api.ResourceGroupProps `json:"props"` - Enabled int `json:"enabled"` - IsDefaultBoolean bool `json:"isDefault"` - Query *api.RGQuery `json:"query"` - Description string `json:"description,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` + Id string `json:"resourceGroupGuid"` + ResType string `json:"type"` + Name string `json:"name"` + Enabled int `json:"enabled"` + IsDefaultBoolean bool `json:"isDefault"` + Query *api.RGQuery `json:"query"` + Description string `json:"description,omitempty"` + UpdatedTime *time.Time `json:"updatedTime,omitempty"` + UpdatedBy string `json:"updatedBy,omitempty"` } From 063ae1320d43cc71c328aa9ae34bb87f4b7afad8 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 07:53:37 -0500 Subject: [PATCH 09/17] fix: json parsing for integ test --- cli/cmd/resource_groups.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index e351d83cc..09ebcbc79 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -302,7 +302,7 @@ type resourceGroup struct { ResType string `json:"type"` Name string `json:"name"` Enabled int `json:"enabled"` - IsDefaultBoolean bool `json:"isDefault"` + IsDefaultBoolean bool `json:"isDefaultBoolean"` Query *api.RGQuery `json:"query"` Description string `json:"description,omitempty"` UpdatedTime *time.Time `json:"updatedTime,omitempty"` From 3bcc07d4d82b24f58f0c5f0417746f3da8efbfcd Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 09:54:47 -0500 Subject: [PATCH 10/17] fix: Bugs. Remove v2 suffix everywhere --- api/_examples/resource-groups/main.go | 47 +----- api/resource_groups.go | 212 +++++++++++--------------- api/resource_groups_v2.go | 164 -------------------- api/v2.go | 4 +- cli/cmd/resource_groups.go | 25 ++- 5 files changed, 114 insertions(+), 338 deletions(-) delete mode 100644 api/resource_groups_v2.go diff --git a/api/_examples/resource-groups/main.go b/api/_examples/resource-groups/main.go index 18e66d8ba..867912031 100644 --- a/api/_examples/resource-groups/main.go +++ b/api/_examples/resource-groups/main.go @@ -25,12 +25,7 @@ func main() { for _, account := range res.Data { var resourceGuid string resourceType := account.Type - - if account.Props != nil { - resourceGuid = account.ResourceGuid - } else { - resourceGuid = account.ResourceGroupGuid - } + resourceGuid = account.ResourceGroupGuid support := "Unsupported" switch resourceType { @@ -42,10 +37,12 @@ func main() { support = "Supported" case api.GcpResourceGroup.String(): support = "Supported" - case api.LwAccountResourceGroup.String(): - support = "Supported" case api.MachineResourceGroup.String(): support = "Supported" + case api.OciResourceGroup.String(): + support = "Supported" + case api.KubernetesResourceGroup.String(): + support = "Supported" } // Output: RESOURCE_GUID:RESOURCE_TYPE:[Supported|Unsupported] @@ -74,7 +71,7 @@ func main() { }, } - myResourceGroupWithQuery := api.NewResourceGroupWithQuery( + myResourceGroupWithQuery := api.NewResourceGroup( "resource-group-with-query-from-golang", api.AwsResourceGroup, "Resource groups in `us` regions", @@ -91,9 +88,9 @@ func main() { fmt.Printf("Succesfully created resource group \n %+v\n", rgV2Resp) println("Updating v2 resource group name") - rgV2Resp.Data.NameV2 = "resource-group-with-query-from-golang-updated" + rgV2Resp.Data.Name = "resource-group-with-query-from-golang-updated" - updatedResponse, err := lacework.V2.ResourceGroups.UpdateAws(&rgV2Resp.Data) + updatedResponse, err := lacework.V2.ResourceGroups.Update(&rgV2Resp.Data) if err != nil { log.Fatal(err) @@ -106,32 +103,4 @@ func main() { log.Fatal(err) } println("Successfully deleted resource group") - - props := api.LwAccountResourceGroupProps{ - Description: "All Lacework accounts", - LwAccounts: []string{"tech-ally"}, - } - - myResourceGroup := api.NewResourceGroup( - "resource-group-from-golang", - api.LwAccountResourceGroup, - props, - ) - - // LW_ACCOUNT resource groups are only allowed at Organization level, - // copy the client to make it an org client - orgLwClient, err := api.CopyClient(lacework, - api.WithOrgAccess(), - ) - if err != nil { - log.Fatal(err) - } - - response, err := orgLwClient.V2.ResourceGroups.Create(myResourceGroup) - if err != nil { - log.Fatal(err) - } - - // Output: Resource Group created: RESOURCE_GUID - fmt.Printf("Resource Group created: %s", response.Data.ResourceGuid) } diff --git a/api/resource_groups.go b/api/resource_groups.go index 403fa01ba..51a229848 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -27,20 +27,6 @@ import ( "github.com/pkg/errors" ) -// ResourceGroupsService is the service that interacts with -// the ResourceGroups schema from the Lacework APIv2 Server -type ResourceGroupsService struct { - client *Client -} - -type ResourceGroup interface { - ID() string - ResourceGroupType() resourceGroupType - ResetResourceGUID() - ResetRGV2Fields() - IsV2Group() bool -} - type resourceGroupType int const ( @@ -67,8 +53,7 @@ var ( //go:embed _templates/resource_groups/gcp.json GcpResourceGroupQueryTemplate string //go:embed _templates/resource_groups/machine.json - MachineResourceGroupQueryTemplate string - LwAccountResourceGroupQueryTemplate string = "" + MachineResourceGroupQueryTemplate string //go:embed _templates/resource_groups/oci.json OciResourceGroupQueryTemplate string //go:embed _templates/resource_groups/kubernetes.json @@ -93,8 +78,8 @@ var ResourceGroupTypes = map[resourceGroupType]resourceGroupContext{ } func NewResourceGroup(name string, iType resourceGroupType, - description string, query *RGQuery) ResourceGroupDataWithQuery { - return ResourceGroupDataWithQuery{ + description string, query *RGQuery) ResourceGroupData { + return ResourceGroupData{ Name: name, Type: iType.String(), Enabled: 1, @@ -103,43 +88,16 @@ func NewResourceGroup(name string, iType resourceGroupType, } } -// String returns the string representation of a Resource Group type -func (i resourceGroupType) String() string { - return ResourceGroupTypes[i].resourceGroupType -} - -// QueryTemplate returns the resource group type's query template -func (i resourceGroupType) QueryTemplate() string { - return ResourceGroupTypes[i].queryTemplate -} - -// FindResourceGroupType looks up inside the list of available resource group types -// the matching type from the provided string, if none, returns NoneResourceGroup -func FindResourceGroupType(typ string) (resourceGroupType, bool) { - for i, ctx := range ResourceGroupTypes { - if typ == ctx.resourceGroupType { - return i, true - } - } - return NoneResourceGroup, false -} - -// List returns a list of Resource Groups func (svc *ResourceGroupsService) List() (response ResourceGroupsResponse, err error) { - var rawResponse resourceGroupsWorkaroundResponse + var rawResponse ResourceGroupsResponse err = svc.client.RequestDecoder("GET", apiV2ResourceGroups, nil, &rawResponse) if err != nil { - return - } - response, err = setResourceGroupsResponse(rawResponse) - if err != nil { - return + return rawResponse, err } - return + return rawResponse, nil } -// Create creates a single Resource Group func (svc *ResourceGroupsService) Create(group ResourceGroupData) ( response ResourceGroupResponse, err error, @@ -148,8 +106,7 @@ func (svc *ResourceGroupsService) Create(group ResourceGroupData) ( return } -// Update updates a single ResourceGroup on the Lacework Server -func (svc *ResourceGroupsService) Update(data ResourceGroup) ( +func (svc *ResourceGroupsService) Update(data *ResourceGroupData) ( response ResourceGroupResponse, err error, ) { @@ -168,51 +125,15 @@ func (svc *ResourceGroupsService) Update(data ResourceGroup) ( return } -func castResourceGroupResponse(data resourceGroupWorkaroundData, response interface{}) error { - group := ResourceGroupResponse{ - Data: ResourceGroupData{ - IsDefaultBoolean: data.IsDefaultBoolean, - ResourceGroupGuid: data.ResourceGroupGuid, - Name: data.Name, - Type: data.Type, - Enabled: data.Enabled, - Query: data.Query, - }, - } - - j, err := json.Marshal(group) - if err != nil { - return err - } - - err = json.Unmarshal(j, &response) - if err != nil { - return err - } - return nil -} - -func setResourceGroupsResponse(workaround resourceGroupsWorkaroundResponse) (ResourceGroupsResponse, error) { - var data []ResourceGroupData - for _, r := range workaround.Data { - group, err := setResourceGroupResponse(r) - if err != nil { - return ResourceGroupsResponse{}, err - } - data = append(data, group.Data) - } - - return ResourceGroupsResponse{Data: data}, nil -} - -func setResourceGroupResponse(response resourceGroupWorkaroundData) (ResourceGroupResponse, - error) { - return ResourceGroupResponse{ - Data: ResourceGroupData(response), - }, nil +func (group ResourceGroupData) ResetResourceGUID() { + group.ResourceGroupGuid = "" + group.UpdatedBy = "" + group.UpdatedTime = nil + group.CreatedBy = "" + group.CreatedTime = nil + group.IsDefaultBoolean = nil } -// Delete deletes a Resource Group that matches the provided resource guid func (svc *ResourceGroupsService) Delete(guid string) error { if guid == "" { return errors.New("specify a resourceGuid") @@ -226,14 +147,6 @@ func (svc *ResourceGroupsService) Delete(guid string) error { ) } -// Get returns a raw response of the Resource Group with the matching resource guid. -// -// To return a more specific Go struct of a Resource Group, use the proper -// method such as GetContainerResourceGroup() where the function name is composed by: -// -// Get(guid) -// -// Where is the Resource Group type. func (svc *ResourceGroupsService) Get(guid string, response interface{}) error { var rawResponse resourceGroupWorkaroundResponse err := svc.get(guid, &rawResponse) @@ -250,7 +163,7 @@ func (svc *ResourceGroupsService) create(data interface{}, response interface{}) func (svc *ResourceGroupsService) get(guid string, response interface{}) error { if guid == "" { - return errors.New("specify an resourceGuid") + return errors.New("specify an resource group guid") } apiPath := fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid) return svc.client.RequestDecoder("GET", apiPath, nil, response) @@ -265,22 +178,85 @@ func (svc *ResourceGroupsService) update(guid string, data interface{}, response return svc.client.RequestEncoderDecoder("PATCH", apiPath, data, response) } -func (group ResourceGroupData) ResourceGroupType() resourceGroupType { - t, _ := FindResourceGroupType(group.Type) - return t +type ResourceGroupsService struct { + client *Client } -func (group ResourceGroupData) ID() string { - return group.ResourceGroupGuid +type RGExpression struct { + Operator string `json:"operator"` + Children []*RGChild `json:"children"` } -func (group *ResourceGroupData) ResetResourceGUID() { - group.ResourceGroupGuid = "" - group.UpdatedBy = "" - group.UpdatedTime = nil - group.CreatedBy = "" - group.CreatedTime = nil - group.IsDefaultBoolean = nil +type RGChild struct { + Operator string `json:"operator,omitempty"` + FilterName string `json:"filterName,omitempty"` + Children []*RGChild `json:"children,omitempty"` +} + +type RGFilter struct { + Field string `json:"field"` + Operation string `json:"operation"` + Values []string `json:"values"` + Key string `json:"key,omitempty"` +} + +type RGQuery struct { + Filters map[string]*RGFilter `json:"filters"` + Expression *RGExpression `json:"expression"` +} + +// String returns the string representation of a Resource Group type +func (i resourceGroupType) String() string { + return ResourceGroupTypes[i].resourceGroupType +} + +// QueryTemplate returns the resource group type's query template +func (i resourceGroupType) QueryTemplate() string { + return ResourceGroupTypes[i].queryTemplate +} + +// FindResourceGroupType looks up inside the list of available resource group types +// the matching type from the provided string, if none, returns NoneResourceGroup +func FindResourceGroupType(typ string) (resourceGroupType, bool) { + for i, ctx := range ResourceGroupTypes { + if typ == ctx.resourceGroupType { + return i, true + } + } + return NoneResourceGroup, false +} + +func castResourceGroupResponse(data resourceGroupWorkaroundData, response interface{}) error { + group := ResourceGroupResponse{ + Data: ResourceGroupData{ + IsDefaultBoolean: data.IsDefaultBoolean, + ResourceGroupGuid: data.ResourceGroupGuid, + Name: data.Name, + Type: data.Type, + Enabled: data.Enabled, + Query: data.Query, + Description: data.Description, + CreatedTime: data.CreatedTime, + CreatedBy: data.CreatedBy, + UpdatedBy: data.UpdatedBy, + UpdatedTime: data.UpdatedTime, + }, + } + + j, err := json.Marshal(group) + if err != nil { + return err + } + + err = json.Unmarshal(j, &response) + if err != nil { + return err + } + return nil +} + +func (group ResourceGroupData) ID() string { + return group.ResourceGroupGuid } func (group ResourceGroupData) Status() string { @@ -303,7 +279,7 @@ type ResourceGroupData struct { Query *RGQuery `json:"query,omitempty"` Description string `json:"description,omitempty"` ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` - CreatedTime *time.Time `json:"lastUpdated,omitempty"` + CreatedTime *time.Time `json:"createdTime,omitempty"` CreatedBy string `json:"createdBy,omitempty"` UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` @@ -317,16 +293,12 @@ type resourceGroupWorkaroundResponse struct { Data resourceGroupWorkaroundData `json:"data"` } -type resourceGroupsWorkaroundResponse struct { - Data []resourceGroupWorkaroundData `json:"data"` -} - type resourceGroupWorkaroundData struct { Name string `json:"name,omitempty"` Query *RGQuery `json:"query,omitempty"` Description string `json:"description,omitempty"` ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` - CreatedTime *time.Time `json:"lastUpdated,omitempty"` + CreatedTime *time.Time `json:"createdTime,omitempty"` CreatedBy string `json:"createdBy,omitempty"` UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` diff --git a/api/resource_groups_v2.go b/api/resource_groups_v2.go deleted file mode 100644 index 04843917e..000000000 --- a/api/resource_groups_v2.go +++ /dev/null @@ -1,164 +0,0 @@ -// -// Author:: Zeki Sherif() -// 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" - "time" - - "github.com/pkg/errors" -) - -func (svc *ResourceGroupsV2Service) List() (response ResourceGroupsV2Response, err error) { - var rawResponse ResourceGroupsV2Response - err = svc.client.RequestDecoder("GET", apiV2ResourceGroups, nil, &rawResponse) - if err != nil { - return rawResponse, err - } - - return rawResponse, nil -} - -func (svc *ResourceGroupsV2Service) Create(group ResourceGroupDataWithQuery) ( - response ResourceGroupV2Response, - err error, -) { - err = svc.create(group, &response) - return -} - -func (svc *ResourceGroupsV2Service) Update(data ResourceGroup) ( - response ResourceGroupV2Response, - err error, -) { - if data == nil { - err = errors.New("resource group must not be empty") - return - } - guid := data.ID() - data.ResetResourceGUID() - - err = svc.update(guid, data, &response) - if err != nil { - return - } - - return -} - -func (svc *ResourceGroupsV2Service) Delete(guid string) error { - if guid == "" { - return errors.New("specify a resourceGuid") - } - - return svc.client.RequestDecoder( - "DELETE", - fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid), - nil, - nil, - ) -} - -func (svc *ResourceGroupsV2Service) Get(guid string, response interface{}) error { - var rawResponse resourceGroupWorkaroundResponse - err := svc.get(guid, &rawResponse) - if err != nil { - return err - } - - return castResourceGroupResponse(rawResponse.Data, &response) -} - -func (svc *ResourceGroupsV2Service) create(data interface{}, response interface{}) error { - return svc.client.RequestEncoderDecoder("POST", apiV2ResourceGroups, data, response) -} - -func (svc *ResourceGroupsV2Service) get(guid string, response interface{}) error { - if guid == "" { - return errors.New("specify an resourceGuid") - } - apiPath := fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid) - return svc.client.RequestDecoder("GET", apiPath, nil, response) -} - -func (svc *ResourceGroupsV2Service) update(guid string, data interface{}, response interface{}) error { - if guid == "" { - return errors.New("specify a resource group guid") - } - - apiPath := fmt.Sprintf(apiV2ResourceGroupsFromGUID, guid) - return svc.client.RequestEncoderDecoder("PATCH", apiPath, data, response) -} - -type ResourceGroupV2Response struct { - Data ResourceGroupDataWithQuery `json:"data"` -} - -type ResourceGroupsV2Response struct { - Data []ResourceGroupDataWithQuery `json:"data"` -} - -type ResourceGroupsV2Service struct { - client *Client -} - -type RGExpression struct { - Operator string `json:"operator"` - Children []*RGChild `json:"children"` -} - -type RGChild struct { - Operator string `json:"operator,omitempty"` - FilterName string `json:"filterName,omitempty"` - Children []*RGChild `json:"children,omitempty"` -} - -type RGFilter struct { - Field string `json:"field"` - Operation string `json:"operation"` - Values []string `json:"values"` - Key string `json:"key,omitempty"` -} - -type RGQuery struct { - Filters map[string]*RGFilter `json:"filters"` - Expression *RGExpression `json:"expression"` -} -type ResourceGroupDataWithQuery struct { - Name string `json:"name"` - Type string `json:"resourceType"` - Query *RGQuery `json:"query"` - Description string `json:"description,omitempty"` - ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` - CreatedTime *time.Time `json:"lastUpdated,omitempty"` - CreatedBy string `json:"createdBy,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` - Enabled int `json:"enabled,omitempty"` - IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - IsOrg *bool `json:"isOrg,omitempty"` -} - -func (group ResourceGroupDataWithQuery) ID() string { - return group.ResourceGroupGuid -} - -func (group ResourceGroupDataWithQuery) IsV2Group() bool { - return true -} diff --git a/api/v2.go b/api/v2.go index b616ff1f1..cb44328f1 100644 --- a/api/v2.go +++ b/api/v2.go @@ -43,7 +43,7 @@ type V2Endpoints struct { ContainerRegistries *ContainerRegistriesService Configs *v2ConfigService FeatureFlags *FeatureFlagsService - ResourceGroups *ResourceGroupsV2Service + ResourceGroups *ResourceGroupsService AgentAccessTokens *AgentAccessTokensService AgentInfo *AgentInfoService Inventory *InventoryService @@ -80,7 +80,7 @@ func NewV2Endpoints(c *Client) *V2Endpoints { &ContainerRegistriesService{c}, NewV2ConfigService(c), &FeatureFlagsService{c}, - &ResourceGroupsV2Service{c}, + &ResourceGroupsService{c}, &AgentAccessTokensService{c}, &AgentInfoService{c}, &InventoryService{c}, diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index 09ebcbc79..91afda38c 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -75,7 +75,7 @@ Then navigate to Settings > Resource Groups. ResType: g.Type, Name: g.Name, Enabled: g.Enabled, - IsDefaultBoolean: *g.IsDefaultBoolean, + IsDefaultBoolean: g.IsDefaultBoolean, Query: g.Query, }) } @@ -90,7 +90,7 @@ Then navigate to Settings > Resource Groups. rows := [][]string{} for _, g := range groups { rows = append(rows, []string{g.Id, g.ResType, g.Name, strconv.Itoa(g.Enabled), - strconv.FormatBool(g.IsDefaultBoolean)}) + strconv.FormatBool(*g.IsDefaultBoolean)}) } cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "STATUS", "DEFAULT"}, rows)) @@ -106,6 +106,7 @@ Then navigate to Settings > Resource Groups. RunE: func(_ *cobra.Command, args []string) error { var response api.ResourceGroupResponse err := cli.LwApi.V2.ResourceGroups.Get(args[0], &response) + if err != nil { return errors.Wrap(err, "unable to get resource group") } @@ -115,11 +116,13 @@ Then navigate to Settings > Resource Groups. ResType: response.Data.Type, Name: response.Data.Name, Enabled: response.Data.Enabled, - IsDefaultBoolean: *response.Data.IsDefaultBoolean, + IsDefaultBoolean: response.Data.IsDefaultBoolean, Query: response.Data.Query, Description: response.Data.Description, UpdatedBy: response.Data.UpdatedBy, UpdatedTime: response.Data.UpdatedTime, + CreatedTime: response.Data.CreatedTime, + CreatedBy: response.Data.CreatedBy, } if cli.JSONOutput() { @@ -133,10 +136,11 @@ Then navigate to Settings > Resource Groups. groupCommon = append(groupCommon, []string{group.Id, group.ResType, group.Name, group.Description, strconv.Itoa(group.Enabled), - strconv.FormatBool(group.IsDefaultBoolean), group.UpdatedBy, group.UpdatedTime.UTC().String()}, + strconv.FormatBool(*group.IsDefaultBoolean), group.CreatedBy, group.CreatedTime.UTC().String(), + group.UpdatedBy, group.UpdatedTime.UTC().String()}, ) cli.OutputHuman(renderSimpleTable([]string{"RESOURCE GROUP ID", "TYPE", "NAME", "DESCRIPTION", "STATE", - "DEFAULT", "UPDATED BY", "UPDATED_TIME"}, groupCommon)) + "DEFAULT", "CREATED BY", "CREATED TIME", "UPDATED BY", "UPDATED TIME"}, groupCommon)) return nil }, @@ -290,21 +294,16 @@ func init() { resourceGroupsCommand.AddCommand(resourceGroupsDeleteCommand) } -func IsDefault(isDefault int) string { - if isDefault == 1 { - return "True" - } - return "False" -} - type resourceGroup struct { Id string `json:"resourceGroupGuid"` ResType string `json:"type"` Name string `json:"name"` Enabled int `json:"enabled"` - IsDefaultBoolean bool `json:"isDefaultBoolean"` + IsDefaultBoolean *bool `json:"isDefaultBoolean"` Query *api.RGQuery `json:"query"` Description string `json:"description,omitempty"` UpdatedTime *time.Time `json:"updatedTime,omitempty"` UpdatedBy string `json:"updatedBy,omitempty"` + CreatedBy string `json:"createdBy,omitempty"` + CreatedTime *time.Time `json:"createdTime,omitempty"` } From bd2c16f674d734a795f58ea9f979c90304984e8e Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 09:57:31 -0500 Subject: [PATCH 11/17] fix: remove commented out code --- cli/cmd/resource_groups.go | 55 -------------------------------------- 1 file changed, 55 deletions(-) diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index 91afda38c..5c7dfaf6e 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -228,61 +228,6 @@ func promptCreateResourceGroup() error { } } -//func buildResourceGroupPropsTable(group resourceGroup) string { -// props := determineResourceGroupProps(group.ResType, group.Props) -// -// return renderOneLineCustomTable("RESOURCE GROUP PROPS", -// renderCustomTable([]string{}, props, -// tableFunc(func(t *tablewriter.Table) { -// t.SetBorder(false) -// t.SetColumnSeparator(" ") -// t.SetAutoWrapText(false) -// t.SetAlignment(tablewriter.ALIGN_LEFT) -// }), -// ), -// tableFunc(func(t *tablewriter.Table) { -// t.SetBorder(false) -// t.SetAutoWrapText(false) -// }), -// ) -//} - -//func determineResourceGroupProps(resType string, props api.ResourceGroupProps) [][]string { -// propsString, err := json.Marshal(props) -// if err != nil { -// return [][]string{} -// } -// details := setBaseProps(props) -// -// switch resType { -// case api.AwsResourceGroup.String(): -// details = append(details, setAwsProps(propsString)) -// case api.AzureResourceGroup.String(): -// details = append(details, setAzureProps(propsString)...) -// case api.ContainerResourceGroup.String(): -// details = append(details, setContainerProps(propsString)...) -// case api.GcpResourceGroup.String(): -// details = append(details, setGcpProps(propsString)...) -// case api.LwAccountResourceGroup.String(): -// details = append(details, setLwAccountProps(propsString)) -// case api.MachineResourceGroup.String(): -// details = append(details, setMachineProps(propsString)) -// } -// -// return details -//} - -//func setBaseProps(props api.ResourceGroupProps) [][]string { -// var ( -// details [][]string -// ) -// lastUpdated := props.GetBaseProps().LastUpdated -// details = append(details, []string{"DESCRIPTION", props.GetBaseProps().Description}) -// details = append(details, []string{"UPDATED BY", props.GetBaseProps().UpdatedBy}) -// details = append(details, []string{"LAST UPDATED", lastUpdated.String()}) -// return details -//} - func init() { // add the resource-group command rootCmd.AddCommand(resourceGroupsCommand) From 739bc7b3599b0b285d402498afd4a09bc1fc1d64 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:06:50 -0500 Subject: [PATCH 12/17] fix: linting --- api/resource_groups.go | 64 +++--------------------------------------- 1 file changed, 4 insertions(+), 60 deletions(-) diff --git a/api/resource_groups.go b/api/resource_groups.go index 51a229848..5e50ce25f 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -20,7 +20,6 @@ package api import ( _ "embed" - "encoding/json" "fmt" "time" @@ -125,7 +124,7 @@ func (svc *ResourceGroupsService) Update(data *ResourceGroupData) ( return } -func (group ResourceGroupData) ResetResourceGUID() { +func (group *ResourceGroupData) ResetResourceGUID() { group.ResourceGroupGuid = "" group.UpdatedBy = "" group.UpdatedTime = nil @@ -148,13 +147,13 @@ func (svc *ResourceGroupsService) Delete(guid string) error { } func (svc *ResourceGroupsService) Get(guid string, response interface{}) error { - var rawResponse resourceGroupWorkaroundResponse + var rawResponse ResourceGroupData err := svc.get(guid, &rawResponse) if err != nil { return err } - return castResourceGroupResponse(rawResponse.Data, &response) + return nil } func (svc *ResourceGroupsService) create(data interface{}, response interface{}) error { @@ -226,46 +225,10 @@ func FindResourceGroupType(typ string) (resourceGroupType, bool) { return NoneResourceGroup, false } -func castResourceGroupResponse(data resourceGroupWorkaroundData, response interface{}) error { - group := ResourceGroupResponse{ - Data: ResourceGroupData{ - IsDefaultBoolean: data.IsDefaultBoolean, - ResourceGroupGuid: data.ResourceGroupGuid, - Name: data.Name, - Type: data.Type, - Enabled: data.Enabled, - Query: data.Query, - Description: data.Description, - CreatedTime: data.CreatedTime, - CreatedBy: data.CreatedBy, - UpdatedBy: data.UpdatedBy, - UpdatedTime: data.UpdatedTime, - }, - } - - j, err := json.Marshal(group) - if err != nil { - return err - } - - err = json.Unmarshal(j, &response) - if err != nil { - return err - } - return nil -} - -func (group ResourceGroupData) ID() string { +func (group *ResourceGroupData) ID() string { return group.ResourceGroupGuid } -func (group ResourceGroupData) Status() string { - if group.Enabled == 1 { - return "Enabled" - } - return "Disabled" -} - type ResourceGroupResponse struct { Data ResourceGroupData `json:"data"` } @@ -287,22 +250,3 @@ type ResourceGroupData struct { Type string `json:"resourceType"` Enabled int `json:"enabled"` } - -// RAIN-21510 workaround -type resourceGroupWorkaroundResponse struct { - Data resourceGroupWorkaroundData `json:"data"` -} - -type resourceGroupWorkaroundData struct { - Name string `json:"name,omitempty"` - Query *RGQuery `json:"query,omitempty"` - Description string `json:"description,omitempty"` - ResourceGroupGuid string `json:"resourceGroupGuid,omitempty"` - CreatedTime *time.Time `json:"createdTime,omitempty"` - CreatedBy string `json:"createdBy,omitempty"` - UpdatedTime *time.Time `json:"updatedTime,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` - IsDefaultBoolean *bool `json:"isDefaultBoolean,omitempty"` - Type string `json:"resourceType"` - Enabled int `json:"enabled,omitempty"` -} From 028fecfa02426e6d9ad76259b8dc55fc851cc16b Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:07:39 -0500 Subject: [PATCH 13/17] fix: integration test --- integration/resource_groups_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/resource_groups_test.go b/integration/resource_groups_test.go index 2f7c1fe03..c5c64a2e9 100644 --- a/integration/resource_groups_test.go +++ b/integration/resource_groups_test.go @@ -38,7 +38,7 @@ func createResourceGroup(typ string, uid string) (string, error) { return "", errors.Wrap(err, "error serializing query template") } - var testResourceGroup api.ResourceGroupDataWithQuery = api.ResourceGroupDataWithQuery{ + var testResourceGroup api.ResourceGroupData = api.ResourceGroupData{ Name: fmt.Sprintf("CLI_TestCreateResourceGroup_%s", iType.String()+"_"+uid), Type: iType.String(), Query: &testQuery, @@ -61,7 +61,7 @@ func createResourceGroup(typ string, uid string) (string, error) { return "", errors.New("non-zero exit code") } - var resourceGroupV2Response api.ResourceGroupV2Response + var resourceGroupV2Response api.ResourceGroupResponse err = json.Unmarshal(out.Bytes(), &resourceGroupV2Response) if err != nil { return "", err From 959a1fa075357c4af062e25235b1d2ebe66323d3 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:08:53 -0500 Subject: [PATCH 14/17] fix: rename vars --- cli/cmd/resource_group_v2.go | 2 +- cli/cmd/resource_groups.go | 14 +++++++------- integration/resource_groups_test.go | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/cmd/resource_group_v2.go b/cli/cmd/resource_group_v2.go index ef01b9f37..8002c82c7 100644 --- a/cli/cmd/resource_group_v2.go +++ b/cli/cmd/resource_group_v2.go @@ -27,7 +27,7 @@ import ( "github.com/lacework/go-sdk/api" ) -func createResourceGroupV2(resourceType string) error { +func createResourceGroup(resourceType string) error { questions := []*survey.Question{ { Name: "name", diff --git a/cli/cmd/resource_groups.go b/cli/cmd/resource_groups.go index 5c7dfaf6e..e3667c7fd 100644 --- a/cli/cmd/resource_groups.go +++ b/cli/cmd/resource_groups.go @@ -210,19 +210,19 @@ func promptCreateResourceGroup() error { switch group { case "AWS": - return createResourceGroupV2("AWS") + return createResourceGroup("AWS") case "AZURE": - return createResourceGroupV2("AZURE") + return createResourceGroup("AZURE") case "GCP": - return createResourceGroupV2("GCP") + return createResourceGroup("GCP") case "CONTAINER": - return createResourceGroupV2("CONTAINER") + return createResourceGroup("CONTAINER") case "MACHINE": - return createResourceGroupV2("MACHINE") + return createResourceGroup("MACHINE") case "OCI": - return createResourceGroupV2("OCI") + return createResourceGroup("OCI") case "KUBERNETES": - return createResourceGroupV2("KUBERNETES") + return createResourceGroup("KUBERNETES") default: return errors.New("unknown resource group type") } diff --git a/integration/resource_groups_test.go b/integration/resource_groups_test.go index c5c64a2e9..acf7c90ff 100644 --- a/integration/resource_groups_test.go +++ b/integration/resource_groups_test.go @@ -61,12 +61,12 @@ func createResourceGroup(typ string, uid string) (string, error) { return "", errors.New("non-zero exit code") } - var resourceGroupV2Response api.ResourceGroupResponse - err = json.Unmarshal(out.Bytes(), &resourceGroupV2Response) + var resourceGroupResponse api.ResourceGroupResponse + err = json.Unmarshal(out.Bytes(), &resourceGroupResponse) if err != nil { return "", err } - return resourceGroupV2Response.Data.ID(), nil + return resourceGroupResponse.Data.ID(), nil } func popResourceGroup() (string, error) { From 5847ef51030b4c55a00ad479b7350c804bed4e73 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:20:19 -0500 Subject: [PATCH 15/17] fix: tests --- api/resource_groups.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/api/resource_groups.go b/api/resource_groups.go index 5e50ce25f..35b15aa9a 100644 --- a/api/resource_groups.go +++ b/api/resource_groups.go @@ -20,6 +20,7 @@ package api import ( _ "embed" + "encoding/json" "fmt" "time" @@ -147,12 +148,21 @@ func (svc *ResourceGroupsService) Delete(guid string) error { } func (svc *ResourceGroupsService) Get(guid string, response interface{}) error { - var rawResponse ResourceGroupData + var rawResponse ResourceGroupResponse err := svc.get(guid, &rawResponse) if err != nil { return err } + j, err := json.Marshal(rawResponse) + if err != nil { + return err + } + + err = json.Unmarshal(j, &response) + if err != nil { + return err + } return nil } From 66c72b4561a29c36c940545c285d17bb12ec5e1d Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:26:49 -0500 Subject: [PATCH 16/17] fix: fix bug in integ test --- integration/resource_groups_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/resource_groups_test.go b/integration/resource_groups_test.go index acf7c90ff..4afb83dd0 100644 --- a/integration/resource_groups_test.go +++ b/integration/resource_groups_test.go @@ -148,7 +148,7 @@ func TestResourceGroupShow(t *testing.T) { t.Run("JSON Output", func(t *testing.T) { out, err, exitcode := LaceworkCLIWithTOMLConfig("resource-group", "show", resourceGroupShowID, "--json") - assert.Contains(t, out.String(), `"resource_guid"`) + assert.Contains(t, out.String(), `"resourceGroupGuid"`) assert.Contains(t, out.String(), `"`+resourceGroupShowID+`"`) assert.Empty(t, err.String(), "STDERR should be empty") assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one") From 08d2bb4c5c978179ceae1bb3d212ac5f10040229 Mon Sep 17 00:00:00 2001 From: Zeki Sherif Date: Wed, 30 Oct 2024 10:32:27 -0500 Subject: [PATCH 17/17] fix: try again to fix integ tests --- api/_examples/resource-groups/main.go | 2 +- integration/resource_groups_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/_examples/resource-groups/main.go b/api/_examples/resource-groups/main.go index 867912031..c1bce30f6 100644 --- a/api/_examples/resource-groups/main.go +++ b/api/_examples/resource-groups/main.go @@ -45,7 +45,7 @@ func main() { support = "Supported" } - // Output: RESOURCE_GUID:RESOURCE_TYPE:[Supported|Unsupported] + // Output: RESOURCE_GROUP_GUID:RESOURCE_TYPE:[Supported|Unsupported] fmt.Printf("%s:%s:%s\n", resourceGuid, resourceType, support) } diff --git a/integration/resource_groups_test.go b/integration/resource_groups_test.go index 4afb83dd0..b3947f23b 100644 --- a/integration/resource_groups_test.go +++ b/integration/resource_groups_test.go @@ -71,7 +71,7 @@ func createResourceGroup(typ string, uid string) (string, error) { func popResourceGroup() (string, error) { type resourceGroup struct { - Id string `json:"resource_guid"` + Id string `json:"resourceGroupGuid"` } type listResourceGroupsResponse struct { ResourceGroups []resourceGroup `json:"resource_groups"` @@ -119,7 +119,7 @@ func TestResourceGroupList(t *testing.T) { // list (output json) out, err, exitcode = LaceworkCLIWithTOMLConfig("resource-group", "list", "--json") - assert.Contains(t, out.String(), `"resource_guid"`) + assert.Contains(t, out.String(), `"resourceGroupGuid"`) assert.Contains(t, out.String(), `"type": "AWS"`) assert.Empty(t, err.String(), "STDERR should be empty") assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one")