Skip to content
Merged
7 changes: 0 additions & 7 deletions internal/service/lakeformation/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ import (
"time"
)

type TableType string

const (
TableTypeTable TableType = "Table"
TableTypeTableWithColumns TableType = "TableWithColumns"
)

const (
TableNameAllTables = "ALL_TABLES"
IAMAllowedPrincipals = "IAM_ALLOWED_PRINCIPALS"
Expand Down
9 changes: 9 additions & 0 deletions internal/service/lakeformation/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ var (
FilterPermissions = filterPermissions

IncludePrincipalIdentifierInList = includePrincipalIdentifierInList

FilterCatalogPermissions = filterCatalogPermissions
FilterDataCellsFilter = filterDataCellsFilter
FilterDataLocationPermissions = filterDataLocationPermissions
FilterDatabasePermissions = filterDatabasePermissions
FilterLFTagPermissions = filterLFTagPermissions
FilterLFTagPolicyPermissions = filterLFTagPolicyPermissions
FilterTablePermissions = filterTablePermissions
FilterTableWithColumnsPermissions = filterTableWithColumnsPermissions
)
218 changes: 51 additions & 167 deletions internal/service/lakeformation/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,64 +5,55 @@ package lakeformation

import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/lakeformation"
awstypes "github.com/aws/aws-sdk-go-v2/service/lakeformation/types"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
)

func filterPermissions(input *lakeformation.ListPermissionsInput, principalIdentifier string, tableType TableType, columnNames []string, excludedColumnNames []string, columnWildcard bool, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
// For most Lake Formation permissions, filtering within the provider is unnecessary. The input
// contains everything for AWS to give you back exactly what you want.
//
// However, IAM Identity Center Group principals cannot be specified in the input, so we must use
// filtering for them.
//
// In addition, many challenges arise with tables and tables with columns.
// Perhaps the two biggest problems (so far) are as follows:
// 1. SELECT - when you grant SELECT, it may be part of a list of permissions. However, when
// listing permissions, SELECT comes back in a separate permission.
// 2. Tables with columns. The ListPermissionsInput does not allow you to include a tables with
// columns resource. This means you might get back more permissions than actually pertain to
// the current situation. The table may have separate permissions that also come back.
//
// Thus, for most cases this is just a pass through filter but attempts to clean out
// permissions in the special cases to avoid extra permissions being included.

if input.Resource.Catalog != nil {
return filterCatalogPermissions(principalIdentifier, allPermissions)
func filterPermissions(filter PermissionsFilter, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
if filter != nil {
return tfslices.Filter(allPermissions, filter)
}

if input.Resource.DataCellsFilter != nil {
return filterDataCellsFilter(principalIdentifier, allPermissions)
}
return nil
}

if input.Resource.DataLocation != nil {
return filterDataLocationPermissions(principalIdentifier, allPermissions)
func filterCatalogPermissions(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.Catalog != nil
}
}

if input.Resource.Database != nil {
return filterDatabasePermissions(principalIdentifier, allPermissions)
func filterDataCellsFilter(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.DataCellsFilter != nil
}
}

if input.Resource.LFTag != nil {
return filterLFTagPermissions(principalIdentifier, allPermissions)
func filterDataLocationPermissions(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.DataLocation != nil
}
}

if input.Resource.LFTagPolicy != nil {
return filterLFTagPolicyPermissions(principalIdentifier, allPermissions)
func filterDatabasePermissions(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.Database != nil
}
}

if tableType == TableTypeTableWithColumns {
return filterTableWithColumnsPermissions(principalIdentifier, input.Resource.Table, columnNames, excludedColumnNames, columnWildcard, allPermissions)
func filterLFTagPermissions(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.LFTag != nil
}
}

if input.Resource.Table != nil || tableType == TableTypeTable {
return filterTablePermissions(principalIdentifier, input.Resource.Table, allPermissions)
func filterLFTagPolicyPermissions(principalIdentifier string) PermissionsFilter {
return func(permissions awstypes.PrincipalResourcePermissions) bool {
return principalIdentifier == aws.ToString(permissions.Principal.DataLakePrincipalIdentifier) && permissions.Resource.LFTagPolicy != nil
}

return nil
}

func filterTablePermissions(principalIdentifier string, table *awstypes.TableResource, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
func filterTablePermissions(principalIdentifier string, table *awstypes.TableResource) PermissionsFilter {
// CREATE PERMS (in) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on Table, Name = (Table Name)
// LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name)
// LIST PERMS (out) = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard
Expand All @@ -71,176 +62,69 @@ func filterTablePermissions(principalIdentifier string, table *awstypes.TableRes
// LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, TableWildcard, Name = ALL_TABLES
// LIST PERMS (out) = SELECT on TableWithColumns, Name = ALL_TABLES, ColumnWildcard

var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
return func(perm awstypes.PrincipalResourcePermissions) bool {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
return false
}

if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil {
if aws.ToString(perm.Resource.TableWithColumns.Name) == aws.ToString(table.Name) || (table.TableWildcard != nil && aws.ToString(perm.Resource.TableWithColumns.Name) == TableNameAllTables) {
if len(perm.Permissions) > 0 && perm.Permissions[0] == awstypes.PermissionSelect {
cleanPermissions = append(cleanPermissions, perm)
continue
return true
}

if len(perm.PermissionsWithGrantOption) > 0 && perm.PermissionsWithGrantOption[0] == awstypes.PermissionSelect {
cleanPermissions = append(cleanPermissions, perm)
continue
return true
}
}
}

if perm.Resource.Table != nil && aws.ToString(perm.Resource.Table.DatabaseName) == aws.ToString(table.DatabaseName) {
if aws.ToString(perm.Resource.Table.Name) == aws.ToString(table.Name) {
cleanPermissions = append(cleanPermissions, perm)
continue
return true
}

if perm.Resource.Table.TableWildcard != nil && table.TableWildcard != nil {
cleanPermissions = append(cleanPermissions, perm)
continue
return true
}
}
continue
}

return cleanPermissions
return false
}
}

func filterTableWithColumnsPermissions(principalIdentifier string, twc *awstypes.TableResource, columnNames []string, excludedColumnNames []string, columnWildcard bool, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
func filterTableWithColumnsPermissions(principalIdentifier string, twc *awstypes.TableResource, columnNames []string, excludedColumnNames []string, columnWildcard bool) PermissionsFilter {
// CREATE PERMS (in) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard
// LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name)
// LIST PERMS (out) = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard

var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
return func(perm awstypes.PrincipalResourcePermissions) bool {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
return false
}

if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnNames != nil {
if StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) {
cleanPermissions = append(cleanPermissions, perm)
continue
if stringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) {
return true
}
}

if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil && (columnWildcard || len(excludedColumnNames) > 0) {
if perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames == nil && len(excludedColumnNames) == 0 {
cleanPermissions = append(cleanPermissions, perm)
continue
return true
}

if len(excludedColumnNames) > 0 && StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames, excludedColumnNames) {
cleanPermissions = append(cleanPermissions, perm)
continue
if len(excludedColumnNames) > 0 && stringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnWildcard.ExcludedColumnNames, excludedColumnNames) {
return true
}
}

if perm.Resource.Table != nil && aws.ToString(perm.Resource.Table.Name) == aws.ToString(twc.Name) {
cleanPermissions = append(cleanPermissions, perm)
continue
}
}

return cleanPermissions
}

func filterCatalogPermissions(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
}

if perm.Resource.Catalog != nil {
cleanPermissions = append(cleanPermissions, perm)
}
}

return cleanPermissions
}

func filterDataCellsFilter(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
}

if perm.Resource.DataCellsFilter != nil {
cleanPermissions = append(cleanPermissions, perm)
}
}

return cleanPermissions
}

func filterDataLocationPermissions(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
}

if perm.Resource.DataLocation != nil {
cleanPermissions = append(cleanPermissions, perm)
}
}

return cleanPermissions
}

func filterDatabasePermissions(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
}

if perm.Resource.Database != nil {
cleanPermissions = append(cleanPermissions, perm)
}
}

return cleanPermissions
}

func filterLFTagPermissions(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
}

if perm.Resource.LFTag != nil {
cleanPermissions = append(cleanPermissions, perm)
}
}

return cleanPermissions
}

func filterLFTagPolicyPermissions(principalIdentifier string, allPermissions []awstypes.PrincipalResourcePermissions) []awstypes.PrincipalResourcePermissions {
var cleanPermissions []awstypes.PrincipalResourcePermissions

for _, perm := range allPermissions {
if principalIdentifier != aws.ToString(perm.Principal.DataLakePrincipalIdentifier) {
continue
if perm.Resource.Table != nil && aws.ToString(perm.Resource.Table.DatabaseName) == aws.ToString(twc.DatabaseName) {
if aws.ToString(perm.Resource.Table.Name) == aws.ToString(twc.Name) {
return true
}
}

if perm.Resource.LFTagPolicy != nil {
cleanPermissions = append(cleanPermissions, perm)
}
return false
}

return cleanPermissions
}
Loading
Loading