Skip to content

Commit

Permalink
fix(cli): split Azure subscriptions with parentheses
Browse files Browse the repository at this point in the history
Listing subscriptions via UI versus CLI has different results,
this change is detecting when users provide a Azure tenant id or
subscription id with their aliases in between parentheses and
it splits them and use only the IDs to download the compliance
report.

Closes ALLY-431

Signed-off-by: Salim Afiune Maya <[email protected]>
  • Loading branch information
afiune committed Apr 13, 2021
1 parent 2dddcb1 commit 790759e
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 14 deletions.
66 changes: 54 additions & 12 deletions cli/cmd/compliance_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,34 @@ Use the following command to list all Azure Tenants configured in your account:
$ lacework compliance az list`,
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
response, err := cli.LwApi.Compliance.ListAzureSubscriptions(args[0])
var (
tenantID, _ = splitIDAndAlias(args[0])
response, err = cli.LwApi.Compliance.ListAzureSubscriptions(tenantID)
)
if err != nil {
return errors.Wrap(err, "unable to list azure subscriptions")
}

if len(response.Data) == 0 {
return errors.New("no data found for the provided tenant")
}

// ALLY-431 Workaround to split the subscription ID and subscription Alias
// ultimately, we need to fix this in the API response
cliCompAzureSubscriptions := splitAzureSubscriptionsApiResponse(response.Data[0])

if cli.JSONOutput() {
return cli.OutputJSON(response.Data[0])
return cli.OutputJSON(cliCompAzureSubscriptions)
}

rows := [][]string{}
for _, azure := range response.Data {
for _, subs := range azure.Subscriptions {
rows = append(rows, []string{subs})
}
for _, subscription := range cliCompAzureSubscriptions.Subscriptions {
rows = append(rows, []string{subscription.ID, subscription.Alias})
}
cli.OutputHuman(renderSimpleTable([]string{"Subscriptions"}, rows))

cli.OutputHuman(renderSimpleTable(
[]string{"Subscription ID", "Subscription Alias"}, rows),
)
return nil
},
}
Expand Down Expand Up @@ -136,11 +148,17 @@ To run an ad-hoc compliance assessment use the command:
`,
Args: cobra.ExactArgs(2),
RunE: func(_ *cobra.Command, args []string) error {
config := api.ComplianceAzureReportConfig{
TenantID: args[0],
SubscriptionID: args[1],
Type: compCmdState.Type,
}
var (
// clean tenantID and subscriptionID if they were provided
// with an Alias in between parentheses
tenantID, _ = splitIDAndAlias(args[0])
subscriptionID, _ = splitIDAndAlias(args[1])
config = api.ComplianceAzureReportConfig{
TenantID: tenantID,
SubscriptionID: subscriptionID,
Type: compCmdState.Type,
}
)

if compCmdState.Pdf {
pdfName := fmt.Sprintf(
Expand Down Expand Up @@ -292,3 +310,27 @@ func complianceAzureReportDetailsTable(report *api.ComplianceAzureReport) [][]st
[]string{"Report Time", report.ReportTime.UTC().Format(time.RFC3339)},
}
}

// ALLY-431 Workaround to split the Subscription ID and Subscription Alias
// ultimately, we need to fix this in the API response
func splitAzureSubscriptionsApiResponse(azInfo api.CompAzureSubscriptions) cliComplianceAzureInfo {
var (
tenantID, tenantAlias = splitIDAndAlias(azInfo.Tenant)
cliAzureInfo = cliComplianceAzureInfo{
Tenant: cliComplianceIDAlias{tenantID, tenantAlias},
Subscriptions: make([]cliComplianceIDAlias, 0),
}
)

for _, subscription := range azInfo.Subscriptions {
id, alias := splitIDAndAlias(subscription)
cliAzureInfo.Subscriptions = append(cliAzureInfo.Subscriptions, cliComplianceIDAlias{id, alias})
}

return cliAzureInfo
}

type cliComplianceAzureInfo struct {
Tenant cliComplianceIDAlias `json:"tenant"`
Subscriptions []cliComplianceIDAlias `json:"subscriptions"`
}
86 changes: 86 additions & 0 deletions cli/cmd/compliance_azure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// Author:: Salim Afiune Maya (<[email protected]>)
// Copyright:: Copyright 2021, Lacework Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package cmd

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

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

func TestSplitAzureSubscriptionsApiResponse(t *testing.T) {
cases := []struct {
subject api.CompAzureSubscriptions
expected cliComplianceAzureInfo
}{
// empty subscriptions will return empty cli info
{
api.CompAzureSubscriptions{},
cliComplianceAzureInfo{Subscriptions: make([]cliComplianceIDAlias, 0)},
},
// real test case with NO alias
{
api.CompAzureSubscriptions{
Tenant: "ABCCC123-abc-123-AB12-XYZ987",
Subscriptions: []string{"subscription-id-1", "subscription-id-2", "subscription-id-3", "subscription-id-4"},
},
cliComplianceAzureInfo{
Tenant: cliComplianceIDAlias{"ABCCC123-abc-123-AB12-XYZ987", ""},
Subscriptions: []cliComplianceIDAlias{
cliComplianceIDAlias{"subscription-id-1", ""},
cliComplianceIDAlias{"subscription-id-2", ""},
cliComplianceIDAlias{"subscription-id-3", ""},
cliComplianceIDAlias{"subscription-id-4", ""},
},
},
},
// real test case with alias
{
api.CompAzureSubscriptions{
Tenant: "ABCCC123-abc-123-AB12-XYZ987 (cool.org.alias.example.com)",
Subscriptions: []string{
"id-1 (a test subscription)",
"xmen-subscription (serious alias)",
"disney-movies (Maybe Production)",
"foo (bar)",
},
},
cliComplianceAzureInfo{
Tenant: cliComplianceIDAlias{"ABCCC123-abc-123-AB12-XYZ987", "cool.org.alias.example.com"},
Subscriptions: []cliComplianceIDAlias{
cliComplianceIDAlias{"id-1", "a test subscription"},
cliComplianceIDAlias{"xmen-subscription", "serious alias"},
cliComplianceIDAlias{"disney-movies", "Maybe Production"},
cliComplianceIDAlias{"foo", "bar"},
},
},
},
}
for i, kase := range cases {
t.Run(fmt.Sprintf("test case %d", i), func(t *testing.T) {
assert.Equalf(t,
kase.expected, splitAzureSubscriptionsApiResponse(kase.subject),
"there is a problem with this test case, please check",
)
})
}
}
5 changes: 4 additions & 1 deletion cli/cmd/compliance_gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ Then, select one GUID from an integration and visualize its details using the co
`,
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
response, err := cli.LwApi.Compliance.ListGcpProjects(args[0])
var (
orgID, _ = splitIDAndAlias(args[0])
response, err = cli.LwApi.Compliance.ListGcpProjects(orgID)
)
if err != nil {
return errors.Wrap(err, "unable to list gcp projects")
}
Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/compliance_gcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestSplitIDAndAlias(t *testing.T) {
}
}

func TestFixGcpProjectsApiResponse(t *testing.T) {
func TestSplitGcpProjectsApiResponse(t *testing.T) {
cases := []struct {
subject api.CompGcpProjects
expected cliComplianceGcpInfo
Expand Down
16 changes: 16 additions & 0 deletions integration/compliance_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,19 @@ func TestComplianceAzureListTenants(t *testing.T) {
assert.Equal(t, 0, exitcode, "EXITCODE is not the expected one")
assert.Empty(t, err.String(), "STDERR should be empty")
}

func TestComplianceAzureGetReportTenantAndSubscriptionWithAlias(t *testing.T) {
out, err, exitcode := LaceworkCLIWithTOMLConfig(
"compliance", "azure", "get-report", "tenant-id (tenant-alias)", "subscription-id (subscription-alias)",
)
assert.Equal(t, 1, exitcode, "EXITCODE is not the expected one")
assert.Contains(t, out.String(),
"Getting compliance report...",
"STDOUT changed, please check")
assert.Contains(t, err.String(),
"unable to get azure compliance report",
"STDERR changed, please check")
assert.Contains(t, err.String(),
"AZURE_SUBS_ID=subscription-id&AZURE_TENANT_ID=tenant-id&",
"STDERR changed, please check")
}

0 comments on commit 790759e

Please sign in to comment.