-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from CheckmarxDev/feature/AST-3450-verify-expor…
…t-permissions Feature/ast 3450 verify export permissions
- Loading branch information
Showing
14 changed files
with
425 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package export | ||
|
||
const ( | ||
UsersOption = "users" | ||
TeamsOption = "teams" | ||
ResultsOption = "results" | ||
) | ||
|
||
func GetOptions() []string { | ||
return []string{UsersOption, TeamsOption, ResultsOption} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package permissions | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/checkmarxDev/ast-sast-export/internal/export" | ||
"github.com/checkmarxDev/ast-sast-export/internal/sliceutils" | ||
"github.com/dgrijalva/jwt-go" | ||
) | ||
|
||
const ( | ||
useOdataPermission = "use-odata" | ||
generateScanReportPermission = "generate-scan-report" | ||
manageAuthProviderPermission = "manage-authentication-providers" | ||
manageRolesPermission = "manage-roles" | ||
) | ||
|
||
var permissionDescription = map[interface{}]string{ | ||
useOdataPermission: "Sast > API > Use Odata", | ||
generateScanReportPermission: "Sast > Reports > Generate Scan Report", | ||
manageAuthProviderPermission: "Access Control > General > Manage Authentication Providers", | ||
manageRolesPermission: "Access Control > General > Manage Roles", | ||
} | ||
|
||
func GetFromExportOptions(exportOptions []string) []interface{} { | ||
var output []string | ||
|
||
usersPermissions := []string{manageAuthProviderPermission, manageRolesPermission} | ||
teamsPermissions := []string{manageAuthProviderPermission} | ||
resultsPermissions := []string{useOdataPermission, generateScanReportPermission} | ||
|
||
for _, exportOption := range exportOptions { | ||
if exportOption == export.UsersOption { | ||
output = append(output, usersPermissions...) | ||
} else if exportOption == export.TeamsOption { | ||
output = append(output, teamsPermissions...) | ||
} else if exportOption == export.ResultsOption { | ||
output = append(output, resultsPermissions...) | ||
} | ||
} | ||
return sliceutils.Unique(sliceutils.ConvertStringToInterface(output)) | ||
} | ||
|
||
func GetFromJwtClaims(jwtClaims jwt.MapClaims, keys []string) ([]interface{}, error) { | ||
permissions := make([]interface{}, 0) | ||
for _, key := range keys { | ||
claimPermissions, permissionErr := getFromJwtClaim(jwtClaims, key) | ||
if permissionErr != nil { | ||
return nil, fmt.Errorf("could not parse %s permissions", key) | ||
} | ||
permissions = append(permissions, claimPermissions...) | ||
} | ||
return permissions, nil | ||
} | ||
|
||
func getFromJwtClaim(claims jwt.MapClaims, key string) ([]interface{}, error) { | ||
claimValue, exists := claims[key] | ||
if !exists { | ||
return make([]interface{}, 0), nil | ||
} | ||
multiplePermissions, ok := claimValue.([]interface{}) | ||
if ok { | ||
return multiplePermissions, nil | ||
} | ||
singlePermission, ok := claimValue.(interface{}) | ||
if ok { | ||
return []interface{}{singlePermission}, nil | ||
} | ||
return make([]interface{}, 0), fmt.Errorf("could not parse permissions") | ||
} | ||
|
||
func GetDescription(permission interface{}) (string, error) { | ||
description, ok := permissionDescription[permission] | ||
if !ok { | ||
return "", fmt.Errorf("unknown permission %s", permission) | ||
} | ||
return description, nil | ||
} | ||
|
||
func GetMissing(required, available []interface{}) []interface{} { | ||
missing := make([]interface{}, 0) | ||
for _, requiredPermission := range required { | ||
if !sliceutils.Contains(requiredPermission, available) { | ||
missing = append(missing, requiredPermission) | ||
} | ||
} | ||
return missing | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
package permissions | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/checkmarxDev/ast-sast-export/internal/export" | ||
|
||
"github.com/dgrijalva/jwt-go" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestGetFromExportOptions(t *testing.T) { | ||
t.Run("users case", func(t *testing.T) { | ||
exportOptions := []string{export.UsersOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission, manageRolesPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("teams case", func(t *testing.T) { | ||
exportOptions := []string{export.TeamsOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("results case", func(t *testing.T) { | ||
exportOptions := []string{export.ResultsOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{useOdataPermission, generateScanReportPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("users+teams case", func(t *testing.T) { | ||
exportOptions := []string{export.UsersOption, export.TeamsOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission, manageRolesPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("users+results case", func(t *testing.T) { | ||
exportOptions := []string{export.UsersOption, export.ResultsOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission, manageRolesPermission, useOdataPermission, generateScanReportPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("results+users case", func(t *testing.T) { | ||
exportOptions := []string{export.ResultsOption, export.UsersOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission, manageRolesPermission, useOdataPermission, generateScanReportPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("results+teams case", func(t *testing.T) { | ||
exportOptions := []string{export.TeamsOption, export.ResultsOption} | ||
result := GetFromExportOptions(exportOptions) | ||
|
||
expected := []interface{}{manageAuthProviderPermission, useOdataPermission, generateScanReportPermission} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
} | ||
|
||
func TestGetAllFromJwtClaims(t *testing.T) { | ||
claims := jwt.MapClaims{ | ||
"aaa": []interface{}{"a", "b"}, | ||
"bbb": []interface{}{"c", "d"}, | ||
"ccc": []interface{}{"e", "f"}, | ||
} | ||
|
||
result, err := GetFromJwtClaims(claims, []string{"aaa", "bbb"}) | ||
|
||
expected := []interface{}{"a", "b", "c", "d"} | ||
assert.NoError(t, err) | ||
assert.ElementsMatch(t, expected, result) | ||
} | ||
|
||
func TestGetFromJwtClaims(t *testing.T) { | ||
key := "permissions" | ||
|
||
t.Run("claims without permission", func(t *testing.T) { | ||
claims := jwt.MapClaims{"test": "test"} | ||
|
||
result, err := getFromJwtClaim(claims, key) | ||
|
||
expected := make([]interface{}, 0) | ||
assert.NoError(t, err) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("claims with one permission", func(t *testing.T) { | ||
claims := jwt.MapClaims{"test": "test", "permissions": "use-odata"} | ||
|
||
result, err := getFromJwtClaim(claims, key) | ||
|
||
expected := []interface{}{"use-odata"} | ||
assert.NoError(t, err) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("claims with more than one permission", func(t *testing.T) { | ||
claims := jwt.MapClaims{"test": "test", "permissions": []interface{}{"use-odata", "generate-scan-report"}} | ||
|
||
result, err := getFromJwtClaim(claims, key) | ||
|
||
expected := []interface{}{"use-odata", "generate-scan-report"} | ||
assert.NoError(t, err) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
} | ||
|
||
func TestGetMissing(t *testing.T) { | ||
t.Run("empty lists return empty list", func(t *testing.T) { | ||
required := make([]interface{}, 0) | ||
available := make([]interface{}, 0) | ||
|
||
result := GetMissing(required, available) | ||
|
||
expected := make([]interface{}, 0) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("empty required return empty list", func(t *testing.T) { | ||
required := make([]interface{}, 0) | ||
available := []interface{}{"a", "b", "c"} | ||
|
||
result := GetMissing(required, available) | ||
|
||
expected := make([]interface{}, 0) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("more available than required return empty list", func(t *testing.T) { | ||
required := []interface{}{"a", "b"} | ||
available := []interface{}{"a", "b", "c"} | ||
|
||
result := GetMissing(required, available) | ||
|
||
expected := make([]interface{}, 0) | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("missing one returns one item", func(t *testing.T) { | ||
required := []interface{}{"a", "b", "c"} | ||
available := []interface{}{"a", "b"} | ||
|
||
result := GetMissing(required, available) | ||
|
||
expected := []interface{}{"c"} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
|
||
t.Run("missing many returns many items", func(t *testing.T) { | ||
required := []interface{}{"a", "b", "c", "d", "e", "f"} | ||
available := []interface{}{"a", "b", "d", "f"} | ||
|
||
result := GetMissing(required, available) | ||
|
||
expected := []interface{}{"c", "e"} | ||
assert.ElementsMatch(t, expected, result) | ||
}) | ||
} |
Oops, something went wrong.