From 1cddc9d0dfa4c81249caa9f4770b014355e0fb39 Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Sun, 7 Jul 2024 17:05:19 +0300 Subject: [PATCH 01/15] added authentication_strength column to conditional access table --- azuread/table_azuread_conditional_access_policy.go | 1 + azuread/transforms.go | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/azuread/table_azuread_conditional_access_policy.go b/azuread/table_azuread_conditional_access_policy.go index e93724d..3fe5b08 100644 --- a/azuread/table_azuread_conditional_access_policy.go +++ b/azuread/table_azuread_conditional_access_policy.go @@ -52,6 +52,7 @@ func tableAzureAdConditionalAccessPolicy(_ context.Context) *plugin.Table { {Name: "applications", Type: proto.ColumnType_JSON, Description: "Applications and user actions included in and excluded from the policy.", Transform: transform.FromMethod("ConditionalAccessPolicyConditionsApplications")}, {Name: "application_enforced_restrictions", Type: proto.ColumnType_JSON, Description: "Session control to enforce application restrictions. Only Exchange Online and Sharepoint Online support this session control.", Transform: transform.FromMethod("ConditionalAccessPolicySessionControlsApplicationEnforcedRestrictions")}, {Name: "built_in_controls", Type: proto.ColumnType_JSON, Description: "List of values of built-in controls required by the policy. Possible values: block, mfa, compliantDevice, domainJoinedDevice, approvedApplication, compliantApplication, passwordChange, unknownFutureValue.", Transform: transform.FromMethod("ConditionalAccessPolicyGrantControlsBuiltInControls")}, + {Name: "authentication_strength", Type: proto.ColumnType_JSON, Description: "List combinations of authentication methods allowed by the policy. For example: password, Federated Multi-Factor, FIDO2 security key", Transform: transform.FromMethod("ConditionalAccessPolicyGrantAuthenticationStrength")}, {Name: "client_app_types", Type: proto.ColumnType_JSON, Description: "Client application types included in the policy. Possible values are: all, browser, mobileAppsAndDesktopClients, exchangeActiveSync, easSupported, other.", Transform: transform.FromMethod("ConditionalAccessPolicyConditionsClientAppTypes")}, {Name: "custom_authentication_factors", Type: proto.ColumnType_JSON, Description: "List of custom controls IDs required by the policy.", Transform: transform.FromMethod("ConditionalAccessPolicyGrantControlsCustomAuthenticationFactors")}, {Name: "cloud_app_security", Type: proto.ColumnType_JSON, Description: "Session control to apply cloud app security.", Transform: transform.FromMethod("ConditionalAccessPolicySessionControlsCloudAppSecurity")}, diff --git a/azuread/transforms.go b/azuread/transforms.go index 4c2cd2b..22bf3c2 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -437,6 +437,16 @@ func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessP return conditionalAccessPolicy.GetGrantControls().GetBuiltInControls() } +func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantAuthenticationStrength() []models.AuthenticationMethodModes { + if conditionalAccessPolicy.GetGrantControls() == nil { + return nil + } + if conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength() == nil { + return nil + } + return conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength().GetAllowedCombinations() +} + func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantControlsCustomAuthenticationFactors() []string { if conditionalAccessPolicy.GetGrantControls() == nil { return nil From de9838365197200b9780ef4a7d80002ce494a02f Mon Sep 17 00:00:00 2001 From: TheRealHouseMouse Date: Wed, 17 Jul 2024 18:46:33 +0300 Subject: [PATCH 02/15] named locations table with display name and create\update time --- azuread/plugin.go | 1 + ...uread_conditional_access_named_location.go | 172 ++++++++++++++++++ azuread/transforms.go | 4 + 3 files changed, 177 insertions(+) create mode 100644 azuread/table_azuread_conditional_access_named_location.go diff --git a/azuread/plugin.go b/azuread/plugin.go index c0eea2a..2f70c7c 100644 --- a/azuread/plugin.go +++ b/azuread/plugin.go @@ -34,6 +34,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azuread_application_app_role_assigned_to": tableAzureAdApplicationAppRoleAssignment(ctx), "azuread_authorization_policy": tableAzureAdAuthorizationPolicy(ctx), "azuread_conditional_access_policy": tableAzureAdConditionalAccessPolicy(ctx), + "azuread_conditional_access_named_location": tableAzureAdConditionalAccessNamedLocation(ctx), "azuread_device": tableAzureAdDevice(ctx), "azuread_directory_audit_report": tableAzureAdDirectoryAuditReport(ctx), "azuread_directory_role": tableAzureAdDirectoryRole(ctx), diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go new file mode 100644 index 0000000..ebf49b8 --- /dev/null +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -0,0 +1,172 @@ +package azuread + +import ( + "context" + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform" + + "github.com/turbot/steampipe-plugin-sdk/v5/plugin" + + msgraphcore "github.com/microsoftgraph/msgraph-sdk-go-core" + "github.com/microsoftgraph/msgraph-sdk-go/identity" + "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +//// TABLE DEFINITION + +func tableAzureAdConditionalAccessNamedLocation(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "azuread_conditional_access_named_location", + Description: "Represents an Azure Active Directory (Azure AD) Conditional Access Named Location.", + Get: &plugin.GetConfig{ + Hydrate: getAdConditionalAccessNamedLocation, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: isIgnorableErrorPredicate([]string{"Request_ResourceNotFound", "Invalid object identifier"}), + }, + KeyColumns: plugin.SingleColumn("id"), + }, + List: &plugin.ListConfig{ + Hydrate: listAdConditionalAccessNamedLocations, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: isIgnorableErrorPredicate([]string{"Request_UnsupportedQuery"}), + }, + KeyColumns: []*plugin.KeyColumn{ + {Name: "display_name", Require: plugin.Optional}, + }, + }, + + Columns: commonColumns([]*plugin.Column{ + {Name: "id", Type: proto.ColumnType_STRING, Description: "Specifies the identifier of a Named Location object.", Transform: transform.FromMethod("GetId")}, + {Name: "display_name", Type: proto.ColumnType_STRING, Description: "Specifies a display name for the Named Location object.", Transform: transform.FromMethod("GetDisplayName")}, + {Name: "created_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The create date of the Named Location object.", Transform: transform.FromMethod("GetCreatedDateTime")}, + {Name: "modified_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The modification date of Named Location object.", Transform: transform.FromMethod("GetModifiedDateTime")}, + }), + } +} + +//// LIST FUNCTION + +func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + // Create client + client, adapter, err := GetGraphClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.listAdConditionalAccessNamedLocations", "connection_error", err) + return nil, err + } + + // List operations + input := &identity.ConditionalAccessNamedLocationsRequestBuilderGetQueryParameters{ + Top: Int32(1000), + } + + limit := d.QueryContext.Limit + if limit != nil { + if *limit > 0 && *limit < 1000 { + l := int32(*limit) + input.Top = Int32(l) + } + } + + equalQuals := d.EqualsQuals + filter := buildConditionalAccessNamedLocationQueryFilter(equalQuals) + + if len(filter) > 0 { + joinStr := strings.Join(filter, " and ") + input.Filter = &joinStr + } + + options := &identity.ConditionalAccessNamedLocationsRequestBuilderGetRequestConfiguration{ + QueryParameters: input, + } + + result, err := client.Identity().ConditionalAccess().NamedLocations().Get(ctx, options) + if err != nil { + errObj := getErrorObject(err) + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "list_conditional_access_named_location_error", errObj) + return nil, errObj + } + + pageIterator, err := msgraphcore.NewPageIterator[models.NamedLocationable](result, adapter, models.CreateNamedLocationCollectionResponseFromDiscriminatorValue) + if err != nil { + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "create_iterator_instance_error", err) + return nil, err + } + + err = pageIterator.Iterate(ctx, func(pageItem models.NamedLocationable) bool { + d.StreamListItem(ctx, &ADLocationInfo{pageItem}) + + // Context can be cancelled due to manual cancellation or the limit has been hit + return d.RowsRemaining(ctx) != 0 + }) + if err != nil { + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "paging_error", err) + return nil, err + } + + return nil, nil +} + +//// HYDRATE FUNCTIONS + +func getAdConditionalAccessNamedLocation(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + + conditionalAccessNamedLocationId := d.EqualsQuals["id"].GetStringValue() + if conditionalAccessNamedLocationId == "" { + return nil, nil + } + + // Create client + client, _, err := GetGraphClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.getAdConditionalAccessNamedLocation", "connection_error", err) + return nil, err + } + + location, err := client.Identity().ConditionalAccess().NamedLocations().ByNamedLocationId(conditionalAccessNamedLocationId).Get(ctx, nil) + if err != nil { + errObj := getErrorObject(err) + plugin.Logger(ctx).Error("getAdConditionalAccessNamedLocation", "get_conditional_access_location_error", errObj) + return nil, errObj + } + return &ADLocationInfo{location}, nil +} + +func buildConditionalAccessNamedLocationQueryFilter(equalQuals plugin.KeyColumnEqualsQualMap) []string { + filters := []string{} + + filterQuals := map[string]string{ + "display_name": "string", + "state": "string", + } + + for qual, qualType := range filterQuals { + switch qualType { + case "string": + if equalQuals[qual] != nil { + filters = append(filters, fmt.Sprintf("%s eq '%s'", strcase.ToCamel(qual), equalQuals[qual].GetStringValue())) + } + } + } + + return filters +} + +//// TRANSFORM FUNCTIONS + +func adConditionalAccessNamedLocationTitle(_ context.Context, d *transform.TransformData) (interface{}, error) { + data := d.HydrateItem.(*ADLocationInfo) + if data == nil { + return nil, nil + } + + title := data.GetDisplayName() + if title == nil { + title = data.GetId() + } + + return title, nil +} diff --git a/azuread/transforms.go b/azuread/transforms.go index 22bf3c2..29b0140 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -59,6 +59,10 @@ type ADIdentityProviderInfo struct { ClientSecret interface{} } +type ADLocationInfo struct { + models.NamedLocationable +} + type ADSecurityDefaultsPolicyInfo struct { models.IdentitySecurityDefaultsEnforcementPolicyable } From 950166b4b00da100289adebde32ce99b1d513e75 Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 14:51:47 +0300 Subject: [PATCH 03/15] Added support for named location conditional access policies --- ...uread_conditional_access_named_location.go | 9 +- azuread/transforms.go | 93 ++++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go index ebf49b8..5b54168 100644 --- a/azuread/table_azuread_conditional_access_named_location.go +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -42,6 +42,8 @@ func tableAzureAdConditionalAccessNamedLocation(_ context.Context) *plugin.Table Columns: commonColumns([]*plugin.Column{ {Name: "id", Type: proto.ColumnType_STRING, Description: "Specifies the identifier of a Named Location object.", Transform: transform.FromMethod("GetId")}, {Name: "display_name", Type: proto.ColumnType_STRING, Description: "Specifies a display name for the Named Location object.", Transform: transform.FromMethod("GetDisplayName")}, + {Name: "location_info", Type: proto.ColumnType_JSON, Description: "Specifies some location information for the Named Location object. Now supported: IP (v4/6 and CIDR/Range), odata_type, IsTrusted (for IP named locations only). Country (and regions, if exist), lookup method, UnkownCountriesAndRegions (for country named locations only)", Transform: transform.FromMethod("GetLocationInfo")}, + {Name: "type", Type: proto.ColumnType_STRING, Description: "Specifies the type of the Named Location object: IP or Country", Transform: transform.FromMethod("GetType")}, {Name: "created_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The create date of the Named Location object.", Transform: transform.FromMethod("GetCreatedDateTime")}, {Name: "modified_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The modification date of Named Location object.", Transform: transform.FromMethod("GetModifiedDateTime")}, }), @@ -97,7 +99,12 @@ func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryD } err = pageIterator.Iterate(ctx, func(pageItem models.NamedLocationable) bool { - d.StreamListItem(ctx, &ADLocationInfo{pageItem}) + switch t := pageItem.(type) { + case *models.IpNamedLocation: + d.StreamListItem(ctx, &ADIpNamedLocationInfo{t}) + case *models.CountryNamedLocation: + d.StreamListItem(ctx, &ADCountryNamedLocationInfo{t}) + } // Context can be cancelled due to manual cancellation or the limit has been hit return d.RowsRemaining(ctx) != 0 diff --git a/azuread/transforms.go b/azuread/transforms.go index 29b0140..80786de 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -63,6 +63,14 @@ type ADLocationInfo struct { models.NamedLocationable } +type ADIpNamedLocationInfo struct { + models.IpNamedLocationable +} + +type ADCountryNamedLocationInfo struct { + models.CountryNamedLocationable +} + type ADSecurityDefaultsPolicyInfo struct { models.IdentitySecurityDefaultsEnforcementPolicyable } @@ -441,13 +449,13 @@ func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessP return conditionalAccessPolicy.GetGrantControls().GetBuiltInControls() } -func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantAuthenticationStrength() []models.AuthenticationMethodModes { +func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantAuthenticationStrength() []models.AuthenticationMethodModes { if conditionalAccessPolicy.GetGrantControls() == nil { return nil } if conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength() == nil { return nil - } + } return conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength().GetAllowedCombinations() } @@ -560,6 +568,87 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } +// NEW!!! +/* +func (ipLocationInfo *ADIpNamedLocationInfo) GetIsIpTrusted() *bool { + return ipLocationInfo.GetIsTrusted() +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetIsIpTrusted() *bool { + return nil +} +*/ +func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { + var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() + var x = make(map[string]interface{}) + max_elements := len(ipRangesArray) + IPv4CidrArr := make([]map[string]interface{}, max_elements) + IPv4RangeArr := make([]map[string]interface{}, max_elements) // The IPv4Range type contains to value: upper address and lower address, + // so we need an array of arrays to represent a list of items of this type + IPv6CidrArr := make([]map[string]interface{}, max_elements) + IPv6RangeArr := make([]map[string]interface{}, max_elements) + + for i := 0; i < len(ipRangesArray); i++ { + switch t := ipRangesArray[i].(type) { + case *models.IPv4CidrRange: + IPv4CidrPair := make(map[string]interface{}) + IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv4CidrPair["OdataType"] = t.GetOdataType() + IPv4CidrArr[i] = IPv4CidrPair + //IPv4CidrArr[i] = *t.GetCidrAddress() + case *models.IPv4Range: + IPv4AddressPair := make(map[string]interface{}) + IPv4AddressPair["Lower"] = *t.GetLowerAddress() + IPv4AddressPair["Upper"] = *t.GetUpperAddress() + IPv4AddressPair["OdataType"] = *t.GetOdataType() + IPv4RangeArr[i] = IPv4AddressPair + case *models.IPv6CidrRange: + IPv6CidrPair := make(map[string]interface{}) + IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv6CidrPair["OdataType"] = t.GetOdataType() + IPv6CidrArr[i] = IPv6CidrPair + //IPv6CidrArr[i] = *t.GetCidrAddress() + case *models.IPv6Range: + IPv6AddressPair := make(map[string]interface{}) // Each address of this type consists of two values + IPv6AddressPair["Lower"] = *t.GetLowerAddress() + IPv6AddressPair["Upper"] = *t.GetUpperAddress() + IPv6AddressPair["OdataType"] = *t.GetOdataType() + IPv6RangeArr[i] = IPv6AddressPair + } + } + x["type"] = "IP" + x["IPv4Cidr"] = IPv4CidrArr + x["IPv4Range"] = IPv4RangeArr + x["IPv6Cidr"] = IPv6CidrArr + x["IPv6Range"] = IPv6RangeArr + x["IsTrusted"] = ipLocationInfo.GetIsTrusted() + return x + /* + res, err := m[0].GetBackingStore().Get("cidrAddress") + if err != nil { + return nil + } + return res.(*string) + */ +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { + var x = make(map[string]interface{}) + x["type"] = "Country" + x["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() + x["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() + x["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() + return x +} + +func (ipLocationInfo *ADIpNamedLocationInfo) GetType() string { + return "IP" +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetType() string { + return "Country" +} + func (directoryAuditReport *ADDirectoryAuditReportInfo) DirectoryAuditAdditionalDetails() []map[string]interface{} { if directoryAuditReport.GetAdditionalDetails() == nil { return nil From bfe34aba73ebd561ba84fb74263ad5a72f96f336 Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 16:16:55 +0300 Subject: [PATCH 04/15] removed odata_type from the JSONs --- azuread/transforms.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 80786de..212c799 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -583,8 +583,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf var x = make(map[string]interface{}) max_elements := len(ipRangesArray) IPv4CidrArr := make([]map[string]interface{}, max_elements) - IPv4RangeArr := make([]map[string]interface{}, max_elements) // The IPv4Range type contains to value: upper address and lower address, - // so we need an array of arrays to represent a list of items of this type + IPv4RangeArr := make([]map[string]interface{}, max_elements) IPv6CidrArr := make([]map[string]interface{}, max_elements) IPv6RangeArr := make([]map[string]interface{}, max_elements) @@ -593,26 +592,20 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf case *models.IPv4CidrRange: IPv4CidrPair := make(map[string]interface{}) IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() - IPv4CidrPair["OdataType"] = t.GetOdataType() IPv4CidrArr[i] = IPv4CidrPair - //IPv4CidrArr[i] = *t.GetCidrAddress() case *models.IPv4Range: IPv4AddressPair := make(map[string]interface{}) IPv4AddressPair["Lower"] = *t.GetLowerAddress() IPv4AddressPair["Upper"] = *t.GetUpperAddress() - IPv4AddressPair["OdataType"] = *t.GetOdataType() IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: IPv6CidrPair := make(map[string]interface{}) IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() - IPv6CidrPair["OdataType"] = t.GetOdataType() IPv6CidrArr[i] = IPv6CidrPair - //IPv6CidrArr[i] = *t.GetCidrAddress() case *models.IPv6Range: - IPv6AddressPair := make(map[string]interface{}) // Each address of this type consists of two values + IPv6AddressPair := make(map[string]interface{}) IPv6AddressPair["Lower"] = *t.GetLowerAddress() IPv6AddressPair["Upper"] = *t.GetUpperAddress() - IPv6AddressPair["OdataType"] = *t.GetOdataType() IPv6RangeArr[i] = IPv6AddressPair } } From e7803301034c71e9a8d3803c95dd215690676a9c Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 16:33:54 +0300 Subject: [PATCH 05/15] refactored code --- azuread/transforms.go | 49 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 212c799..75cf376 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -568,19 +568,9 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } -// NEW!!! -/* -func (ipLocationInfo *ADIpNamedLocationInfo) GetIsIpTrusted() *bool { - return ipLocationInfo.GetIsTrusted() -} - -func (countryLocationInfo *ADCountryNamedLocationInfo) GetIsIpTrusted() *bool { - return nil -} -*/ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() - var x = make(map[string]interface{}) + var locationInfoJSON = make(map[string]interface{}) max_elements := len(ipRangesArray) IPv4CidrArr := make([]map[string]interface{}, max_elements) IPv4RangeArr := make([]map[string]interface{}, max_elements) @@ -591,7 +581,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf switch t := ipRangesArray[i].(type) { case *models.IPv4CidrRange: IPv4CidrPair := make(map[string]interface{}) - IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv4CidrPair["Address"] = *t.GetCidrAddress() IPv4CidrArr[i] = IPv4CidrPair case *models.IPv4Range: IPv4AddressPair := make(map[string]interface{}) @@ -600,7 +590,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: IPv6CidrPair := make(map[string]interface{}) - IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv6CidrPair["Address"] = *t.GetCidrAddress() IPv6CidrArr[i] = IPv6CidrPair case *models.IPv6Range: IPv6AddressPair := make(map[string]interface{}) @@ -609,29 +599,22 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf IPv6RangeArr[i] = IPv6AddressPair } } - x["type"] = "IP" - x["IPv4Cidr"] = IPv4CidrArr - x["IPv4Range"] = IPv4RangeArr - x["IPv6Cidr"] = IPv6CidrArr - x["IPv6Range"] = IPv6RangeArr - x["IsTrusted"] = ipLocationInfo.GetIsTrusted() - return x - /* - res, err := m[0].GetBackingStore().Get("cidrAddress") - if err != nil { - return nil - } - return res.(*string) - */ + locationInfoJSON["type"] = "IP" + locationInfoJSON["IPv4Cidr"] = IPv4CidrArr + locationInfoJSON["IPv4Range"] = IPv4RangeArr + locationInfoJSON["IPv6Cidr"] = IPv6CidrArr + locationInfoJSON["IPv6Range"] = IPv6RangeArr + locationInfoJSON["IsTrusted"] = ipLocationInfo.GetIsTrusted() + return locationInfoJSON } func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var x = make(map[string]interface{}) - x["type"] = "Country" - x["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() - x["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() - x["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() - return x + var locationInfoJSON = make(map[string]interface{}) + locationInfoJSON["type"] = "Country" + locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() + locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() + locationInfoJSON["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() + return locationInfoJSON } func (ipLocationInfo *ADIpNamedLocationInfo) GetType() string { From c9dd1afc983258ba6640100793ee0ab6cae72e9f Mon Sep 17 00:00:00 2001 From: House Mouse Date: Mon, 5 Aug 2024 14:51:28 +0300 Subject: [PATCH 06/15] deleted var and make --- azuread/transforms.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 75cf376..9c56ee1 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -569,36 +569,37 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { } func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() - var locationInfoJSON = make(map[string]interface{}) - max_elements := len(ipRangesArray) - IPv4CidrArr := make([]map[string]interface{}, max_elements) - IPv4RangeArr := make([]map[string]interface{}, max_elements) - IPv6CidrArr := make([]map[string]interface{}, max_elements) - IPv6RangeArr := make([]map[string]interface{}, max_elements) + ipRangesArray := ipLocationInfo.GetIpRanges() + locationInfoJSON := map[string]interface{}{} + + IPv4CidrArr := []map[string]interface{}{} + IPv4RangeArr := []map[string]interface{}{} + IPv6CidrArr := []map[string]interface{}{} + IPv6RangeArr := m[]map[string]interface{}{} for i := 0; i < len(ipRangesArray); i++ { switch t := ipRangesArray[i].(type) { case *models.IPv4CidrRange: - IPv4CidrPair := make(map[string]interface{}) + IPv4CidrPair := map[string]interface{}{} IPv4CidrPair["Address"] = *t.GetCidrAddress() IPv4CidrArr[i] = IPv4CidrPair case *models.IPv4Range: - IPv4AddressPair := make(map[string]interface{}) + IPv4AddressPair := map[string]interface{}{} IPv4AddressPair["Lower"] = *t.GetLowerAddress() IPv4AddressPair["Upper"] = *t.GetUpperAddress() IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: - IPv6CidrPair := make(map[string]interface{}) + IPv6CidrPair := map[string]interface{}{} IPv6CidrPair["Address"] = *t.GetCidrAddress() IPv6CidrArr[i] = IPv6CidrPair case *models.IPv6Range: - IPv6AddressPair := make(map[string]interface{}) + IPv6AddressPair := map[string]interface{}{} IPv6AddressPair["Lower"] = *t.GetLowerAddress() IPv6AddressPair["Upper"] = *t.GetUpperAddress() IPv6RangeArr[i] = IPv6AddressPair } } + locationInfoJSON["type"] = "IP" locationInfoJSON["IPv4Cidr"] = IPv4CidrArr locationInfoJSON["IPv4Range"] = IPv4RangeArr @@ -609,7 +610,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf } func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var locationInfoJSON = make(map[string]interface{}) + locationInfoJSON := map[string]interface{}{} locationInfoJSON["type"] = "Country" locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() From 1dcb6808af43907d5cf5951d1d68f37899ba03fd Mon Sep 17 00:00:00 2001 From: TheRealHouseMouse Date: Wed, 17 Jul 2024 18:46:33 +0300 Subject: [PATCH 07/15] named locations table with display name and create\update time --- azuread/plugin.go | 1 + ...uread_conditional_access_named_location.go | 172 ++++++++++++++++++ azuread/transforms.go | 4 + 3 files changed, 177 insertions(+) create mode 100644 azuread/table_azuread_conditional_access_named_location.go diff --git a/azuread/plugin.go b/azuread/plugin.go index c0eea2a..2f70c7c 100644 --- a/azuread/plugin.go +++ b/azuread/plugin.go @@ -34,6 +34,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azuread_application_app_role_assigned_to": tableAzureAdApplicationAppRoleAssignment(ctx), "azuread_authorization_policy": tableAzureAdAuthorizationPolicy(ctx), "azuread_conditional_access_policy": tableAzureAdConditionalAccessPolicy(ctx), + "azuread_conditional_access_named_location": tableAzureAdConditionalAccessNamedLocation(ctx), "azuread_device": tableAzureAdDevice(ctx), "azuread_directory_audit_report": tableAzureAdDirectoryAuditReport(ctx), "azuread_directory_role": tableAzureAdDirectoryRole(ctx), diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go new file mode 100644 index 0000000..ebf49b8 --- /dev/null +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -0,0 +1,172 @@ +package azuread + +import ( + "context" + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform" + + "github.com/turbot/steampipe-plugin-sdk/v5/plugin" + + msgraphcore "github.com/microsoftgraph/msgraph-sdk-go-core" + "github.com/microsoftgraph/msgraph-sdk-go/identity" + "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +//// TABLE DEFINITION + +func tableAzureAdConditionalAccessNamedLocation(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "azuread_conditional_access_named_location", + Description: "Represents an Azure Active Directory (Azure AD) Conditional Access Named Location.", + Get: &plugin.GetConfig{ + Hydrate: getAdConditionalAccessNamedLocation, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: isIgnorableErrorPredicate([]string{"Request_ResourceNotFound", "Invalid object identifier"}), + }, + KeyColumns: plugin.SingleColumn("id"), + }, + List: &plugin.ListConfig{ + Hydrate: listAdConditionalAccessNamedLocations, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: isIgnorableErrorPredicate([]string{"Request_UnsupportedQuery"}), + }, + KeyColumns: []*plugin.KeyColumn{ + {Name: "display_name", Require: plugin.Optional}, + }, + }, + + Columns: commonColumns([]*plugin.Column{ + {Name: "id", Type: proto.ColumnType_STRING, Description: "Specifies the identifier of a Named Location object.", Transform: transform.FromMethod("GetId")}, + {Name: "display_name", Type: proto.ColumnType_STRING, Description: "Specifies a display name for the Named Location object.", Transform: transform.FromMethod("GetDisplayName")}, + {Name: "created_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The create date of the Named Location object.", Transform: transform.FromMethod("GetCreatedDateTime")}, + {Name: "modified_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The modification date of Named Location object.", Transform: transform.FromMethod("GetModifiedDateTime")}, + }), + } +} + +//// LIST FUNCTION + +func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + // Create client + client, adapter, err := GetGraphClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.listAdConditionalAccessNamedLocations", "connection_error", err) + return nil, err + } + + // List operations + input := &identity.ConditionalAccessNamedLocationsRequestBuilderGetQueryParameters{ + Top: Int32(1000), + } + + limit := d.QueryContext.Limit + if limit != nil { + if *limit > 0 && *limit < 1000 { + l := int32(*limit) + input.Top = Int32(l) + } + } + + equalQuals := d.EqualsQuals + filter := buildConditionalAccessNamedLocationQueryFilter(equalQuals) + + if len(filter) > 0 { + joinStr := strings.Join(filter, " and ") + input.Filter = &joinStr + } + + options := &identity.ConditionalAccessNamedLocationsRequestBuilderGetRequestConfiguration{ + QueryParameters: input, + } + + result, err := client.Identity().ConditionalAccess().NamedLocations().Get(ctx, options) + if err != nil { + errObj := getErrorObject(err) + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "list_conditional_access_named_location_error", errObj) + return nil, errObj + } + + pageIterator, err := msgraphcore.NewPageIterator[models.NamedLocationable](result, adapter, models.CreateNamedLocationCollectionResponseFromDiscriminatorValue) + if err != nil { + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "create_iterator_instance_error", err) + return nil, err + } + + err = pageIterator.Iterate(ctx, func(pageItem models.NamedLocationable) bool { + d.StreamListItem(ctx, &ADLocationInfo{pageItem}) + + // Context can be cancelled due to manual cancellation or the limit has been hit + return d.RowsRemaining(ctx) != 0 + }) + if err != nil { + plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "paging_error", err) + return nil, err + } + + return nil, nil +} + +//// HYDRATE FUNCTIONS + +func getAdConditionalAccessNamedLocation(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + + conditionalAccessNamedLocationId := d.EqualsQuals["id"].GetStringValue() + if conditionalAccessNamedLocationId == "" { + return nil, nil + } + + // Create client + client, _, err := GetGraphClient(ctx, d) + if err != nil { + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.getAdConditionalAccessNamedLocation", "connection_error", err) + return nil, err + } + + location, err := client.Identity().ConditionalAccess().NamedLocations().ByNamedLocationId(conditionalAccessNamedLocationId).Get(ctx, nil) + if err != nil { + errObj := getErrorObject(err) + plugin.Logger(ctx).Error("getAdConditionalAccessNamedLocation", "get_conditional_access_location_error", errObj) + return nil, errObj + } + return &ADLocationInfo{location}, nil +} + +func buildConditionalAccessNamedLocationQueryFilter(equalQuals plugin.KeyColumnEqualsQualMap) []string { + filters := []string{} + + filterQuals := map[string]string{ + "display_name": "string", + "state": "string", + } + + for qual, qualType := range filterQuals { + switch qualType { + case "string": + if equalQuals[qual] != nil { + filters = append(filters, fmt.Sprintf("%s eq '%s'", strcase.ToCamel(qual), equalQuals[qual].GetStringValue())) + } + } + } + + return filters +} + +//// TRANSFORM FUNCTIONS + +func adConditionalAccessNamedLocationTitle(_ context.Context, d *transform.TransformData) (interface{}, error) { + data := d.HydrateItem.(*ADLocationInfo) + if data == nil { + return nil, nil + } + + title := data.GetDisplayName() + if title == nil { + title = data.GetId() + } + + return title, nil +} diff --git a/azuread/transforms.go b/azuread/transforms.go index 22bf3c2..29b0140 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -59,6 +59,10 @@ type ADIdentityProviderInfo struct { ClientSecret interface{} } +type ADLocationInfo struct { + models.NamedLocationable +} + type ADSecurityDefaultsPolicyInfo struct { models.IdentitySecurityDefaultsEnforcementPolicyable } From 7baecb13d67fa392425e1d1ff7610f78631acccd Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 14:51:47 +0300 Subject: [PATCH 08/15] Added support for named location conditional access policies --- ...uread_conditional_access_named_location.go | 9 +- azuread/transforms.go | 93 ++++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go index ebf49b8..5b54168 100644 --- a/azuread/table_azuread_conditional_access_named_location.go +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -42,6 +42,8 @@ func tableAzureAdConditionalAccessNamedLocation(_ context.Context) *plugin.Table Columns: commonColumns([]*plugin.Column{ {Name: "id", Type: proto.ColumnType_STRING, Description: "Specifies the identifier of a Named Location object.", Transform: transform.FromMethod("GetId")}, {Name: "display_name", Type: proto.ColumnType_STRING, Description: "Specifies a display name for the Named Location object.", Transform: transform.FromMethod("GetDisplayName")}, + {Name: "location_info", Type: proto.ColumnType_JSON, Description: "Specifies some location information for the Named Location object. Now supported: IP (v4/6 and CIDR/Range), odata_type, IsTrusted (for IP named locations only). Country (and regions, if exist), lookup method, UnkownCountriesAndRegions (for country named locations only)", Transform: transform.FromMethod("GetLocationInfo")}, + {Name: "type", Type: proto.ColumnType_STRING, Description: "Specifies the type of the Named Location object: IP or Country", Transform: transform.FromMethod("GetType")}, {Name: "created_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The create date of the Named Location object.", Transform: transform.FromMethod("GetCreatedDateTime")}, {Name: "modified_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The modification date of Named Location object.", Transform: transform.FromMethod("GetModifiedDateTime")}, }), @@ -97,7 +99,12 @@ func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryD } err = pageIterator.Iterate(ctx, func(pageItem models.NamedLocationable) bool { - d.StreamListItem(ctx, &ADLocationInfo{pageItem}) + switch t := pageItem.(type) { + case *models.IpNamedLocation: + d.StreamListItem(ctx, &ADIpNamedLocationInfo{t}) + case *models.CountryNamedLocation: + d.StreamListItem(ctx, &ADCountryNamedLocationInfo{t}) + } // Context can be cancelled due to manual cancellation or the limit has been hit return d.RowsRemaining(ctx) != 0 diff --git a/azuread/transforms.go b/azuread/transforms.go index 29b0140..80786de 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -63,6 +63,14 @@ type ADLocationInfo struct { models.NamedLocationable } +type ADIpNamedLocationInfo struct { + models.IpNamedLocationable +} + +type ADCountryNamedLocationInfo struct { + models.CountryNamedLocationable +} + type ADSecurityDefaultsPolicyInfo struct { models.IdentitySecurityDefaultsEnforcementPolicyable } @@ -441,13 +449,13 @@ func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessP return conditionalAccessPolicy.GetGrantControls().GetBuiltInControls() } -func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantAuthenticationStrength() []models.AuthenticationMethodModes { +func (conditionalAccessPolicy *ADConditionalAccessPolicyInfo) ConditionalAccessPolicyGrantAuthenticationStrength() []models.AuthenticationMethodModes { if conditionalAccessPolicy.GetGrantControls() == nil { return nil } if conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength() == nil { return nil - } + } return conditionalAccessPolicy.GetGrantControls().GetAuthenticationStrength().GetAllowedCombinations() } @@ -560,6 +568,87 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } +// NEW!!! +/* +func (ipLocationInfo *ADIpNamedLocationInfo) GetIsIpTrusted() *bool { + return ipLocationInfo.GetIsTrusted() +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetIsIpTrusted() *bool { + return nil +} +*/ +func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { + var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() + var x = make(map[string]interface{}) + max_elements := len(ipRangesArray) + IPv4CidrArr := make([]map[string]interface{}, max_elements) + IPv4RangeArr := make([]map[string]interface{}, max_elements) // The IPv4Range type contains to value: upper address and lower address, + // so we need an array of arrays to represent a list of items of this type + IPv6CidrArr := make([]map[string]interface{}, max_elements) + IPv6RangeArr := make([]map[string]interface{}, max_elements) + + for i := 0; i < len(ipRangesArray); i++ { + switch t := ipRangesArray[i].(type) { + case *models.IPv4CidrRange: + IPv4CidrPair := make(map[string]interface{}) + IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv4CidrPair["OdataType"] = t.GetOdataType() + IPv4CidrArr[i] = IPv4CidrPair + //IPv4CidrArr[i] = *t.GetCidrAddress() + case *models.IPv4Range: + IPv4AddressPair := make(map[string]interface{}) + IPv4AddressPair["Lower"] = *t.GetLowerAddress() + IPv4AddressPair["Upper"] = *t.GetUpperAddress() + IPv4AddressPair["OdataType"] = *t.GetOdataType() + IPv4RangeArr[i] = IPv4AddressPair + case *models.IPv6CidrRange: + IPv6CidrPair := make(map[string]interface{}) + IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv6CidrPair["OdataType"] = t.GetOdataType() + IPv6CidrArr[i] = IPv6CidrPair + //IPv6CidrArr[i] = *t.GetCidrAddress() + case *models.IPv6Range: + IPv6AddressPair := make(map[string]interface{}) // Each address of this type consists of two values + IPv6AddressPair["Lower"] = *t.GetLowerAddress() + IPv6AddressPair["Upper"] = *t.GetUpperAddress() + IPv6AddressPair["OdataType"] = *t.GetOdataType() + IPv6RangeArr[i] = IPv6AddressPair + } + } + x["type"] = "IP" + x["IPv4Cidr"] = IPv4CidrArr + x["IPv4Range"] = IPv4RangeArr + x["IPv6Cidr"] = IPv6CidrArr + x["IPv6Range"] = IPv6RangeArr + x["IsTrusted"] = ipLocationInfo.GetIsTrusted() + return x + /* + res, err := m[0].GetBackingStore().Get("cidrAddress") + if err != nil { + return nil + } + return res.(*string) + */ +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { + var x = make(map[string]interface{}) + x["type"] = "Country" + x["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() + x["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() + x["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() + return x +} + +func (ipLocationInfo *ADIpNamedLocationInfo) GetType() string { + return "IP" +} + +func (countryLocationInfo *ADCountryNamedLocationInfo) GetType() string { + return "Country" +} + func (directoryAuditReport *ADDirectoryAuditReportInfo) DirectoryAuditAdditionalDetails() []map[string]interface{} { if directoryAuditReport.GetAdditionalDetails() == nil { return nil From e2899f53bb59ed4d51153a1bdb6b8d01e08c427a Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 16:16:55 +0300 Subject: [PATCH 09/15] removed odata_type from the JSONs --- azuread/transforms.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 80786de..212c799 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -583,8 +583,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf var x = make(map[string]interface{}) max_elements := len(ipRangesArray) IPv4CidrArr := make([]map[string]interface{}, max_elements) - IPv4RangeArr := make([]map[string]interface{}, max_elements) // The IPv4Range type contains to value: upper address and lower address, - // so we need an array of arrays to represent a list of items of this type + IPv4RangeArr := make([]map[string]interface{}, max_elements) IPv6CidrArr := make([]map[string]interface{}, max_elements) IPv6RangeArr := make([]map[string]interface{}, max_elements) @@ -593,26 +592,20 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf case *models.IPv4CidrRange: IPv4CidrPair := make(map[string]interface{}) IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() - IPv4CidrPair["OdataType"] = t.GetOdataType() IPv4CidrArr[i] = IPv4CidrPair - //IPv4CidrArr[i] = *t.GetCidrAddress() case *models.IPv4Range: IPv4AddressPair := make(map[string]interface{}) IPv4AddressPair["Lower"] = *t.GetLowerAddress() IPv4AddressPair["Upper"] = *t.GetUpperAddress() - IPv4AddressPair["OdataType"] = *t.GetOdataType() IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: IPv6CidrPair := make(map[string]interface{}) IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() - IPv6CidrPair["OdataType"] = t.GetOdataType() IPv6CidrArr[i] = IPv6CidrPair - //IPv6CidrArr[i] = *t.GetCidrAddress() case *models.IPv6Range: - IPv6AddressPair := make(map[string]interface{}) // Each address of this type consists of two values + IPv6AddressPair := make(map[string]interface{}) IPv6AddressPair["Lower"] = *t.GetLowerAddress() IPv6AddressPair["Upper"] = *t.GetUpperAddress() - IPv6AddressPair["OdataType"] = *t.GetOdataType() IPv6RangeArr[i] = IPv6AddressPair } } From 11225a2b7a2379afad2f403f670f6081b9606971 Mon Sep 17 00:00:00 2001 From: anon4mouse Date: Mon, 29 Jul 2024 16:33:54 +0300 Subject: [PATCH 10/15] refactored code --- azuread/transforms.go | 49 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 212c799..75cf376 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -568,19 +568,9 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } -// NEW!!! -/* -func (ipLocationInfo *ADIpNamedLocationInfo) GetIsIpTrusted() *bool { - return ipLocationInfo.GetIsTrusted() -} - -func (countryLocationInfo *ADCountryNamedLocationInfo) GetIsIpTrusted() *bool { - return nil -} -*/ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() - var x = make(map[string]interface{}) + var locationInfoJSON = make(map[string]interface{}) max_elements := len(ipRangesArray) IPv4CidrArr := make([]map[string]interface{}, max_elements) IPv4RangeArr := make([]map[string]interface{}, max_elements) @@ -591,7 +581,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf switch t := ipRangesArray[i].(type) { case *models.IPv4CidrRange: IPv4CidrPair := make(map[string]interface{}) - IPv4CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv4CidrPair["Address"] = *t.GetCidrAddress() IPv4CidrArr[i] = IPv4CidrPair case *models.IPv4Range: IPv4AddressPair := make(map[string]interface{}) @@ -600,7 +590,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: IPv6CidrPair := make(map[string]interface{}) - IPv6CidrPair["CidrAddress"] = *t.GetCidrAddress() + IPv6CidrPair["Address"] = *t.GetCidrAddress() IPv6CidrArr[i] = IPv6CidrPair case *models.IPv6Range: IPv6AddressPair := make(map[string]interface{}) @@ -609,29 +599,22 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf IPv6RangeArr[i] = IPv6AddressPair } } - x["type"] = "IP" - x["IPv4Cidr"] = IPv4CidrArr - x["IPv4Range"] = IPv4RangeArr - x["IPv6Cidr"] = IPv6CidrArr - x["IPv6Range"] = IPv6RangeArr - x["IsTrusted"] = ipLocationInfo.GetIsTrusted() - return x - /* - res, err := m[0].GetBackingStore().Get("cidrAddress") - if err != nil { - return nil - } - return res.(*string) - */ + locationInfoJSON["type"] = "IP" + locationInfoJSON["IPv4Cidr"] = IPv4CidrArr + locationInfoJSON["IPv4Range"] = IPv4RangeArr + locationInfoJSON["IPv6Cidr"] = IPv6CidrArr + locationInfoJSON["IPv6Range"] = IPv6RangeArr + locationInfoJSON["IsTrusted"] = ipLocationInfo.GetIsTrusted() + return locationInfoJSON } func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var x = make(map[string]interface{}) - x["type"] = "Country" - x["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() - x["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() - x["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() - return x + var locationInfoJSON = make(map[string]interface{}) + locationInfoJSON["type"] = "Country" + locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() + locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() + locationInfoJSON["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() + return locationInfoJSON } func (ipLocationInfo *ADIpNamedLocationInfo) GetType() string { From 300572dbab1793c94168fc6f434939e778771306 Mon Sep 17 00:00:00 2001 From: House Mouse Date: Mon, 5 Aug 2024 14:51:28 +0300 Subject: [PATCH 11/15] deleted var and make --- azuread/transforms.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 75cf376..9c56ee1 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -569,36 +569,37 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { } func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var ipRangesArray []models.IpRangeable = ipLocationInfo.GetIpRanges() - var locationInfoJSON = make(map[string]interface{}) - max_elements := len(ipRangesArray) - IPv4CidrArr := make([]map[string]interface{}, max_elements) - IPv4RangeArr := make([]map[string]interface{}, max_elements) - IPv6CidrArr := make([]map[string]interface{}, max_elements) - IPv6RangeArr := make([]map[string]interface{}, max_elements) + ipRangesArray := ipLocationInfo.GetIpRanges() + locationInfoJSON := map[string]interface{}{} + + IPv4CidrArr := []map[string]interface{}{} + IPv4RangeArr := []map[string]interface{}{} + IPv6CidrArr := []map[string]interface{}{} + IPv6RangeArr := m[]map[string]interface{}{} for i := 0; i < len(ipRangesArray); i++ { switch t := ipRangesArray[i].(type) { case *models.IPv4CidrRange: - IPv4CidrPair := make(map[string]interface{}) + IPv4CidrPair := map[string]interface{}{} IPv4CidrPair["Address"] = *t.GetCidrAddress() IPv4CidrArr[i] = IPv4CidrPair case *models.IPv4Range: - IPv4AddressPair := make(map[string]interface{}) + IPv4AddressPair := map[string]interface{}{} IPv4AddressPair["Lower"] = *t.GetLowerAddress() IPv4AddressPair["Upper"] = *t.GetUpperAddress() IPv4RangeArr[i] = IPv4AddressPair case *models.IPv6CidrRange: - IPv6CidrPair := make(map[string]interface{}) + IPv6CidrPair := map[string]interface{}{} IPv6CidrPair["Address"] = *t.GetCidrAddress() IPv6CidrArr[i] = IPv6CidrPair case *models.IPv6Range: - IPv6AddressPair := make(map[string]interface{}) + IPv6AddressPair := map[string]interface{}{} IPv6AddressPair["Lower"] = *t.GetLowerAddress() IPv6AddressPair["Upper"] = *t.GetUpperAddress() IPv6RangeArr[i] = IPv6AddressPair } } + locationInfoJSON["type"] = "IP" locationInfoJSON["IPv4Cidr"] = IPv4CidrArr locationInfoJSON["IPv4Range"] = IPv4RangeArr @@ -609,7 +610,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf } func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { - var locationInfoJSON = make(map[string]interface{}) + locationInfoJSON := map[string]interface{}{} locationInfoJSON["type"] = "Country" locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() From 289e9603b6a57a7172bdfb7244e7247a18163191 Mon Sep 17 00:00:00 2001 From: House Mouse Date: Thu, 8 Aug 2024 11:12:09 +0300 Subject: [PATCH 12/15] solve linting errors and added readme --- ...uread_conditional_access_named_location.go | 16 ----- azuread/transforms.go | 2 +- ...uread_conditional_access_named_location.md | 62 +++++++++++++++++++ 3 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 docs/tables/azuread_conditional_access_named_location.md diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go index 5b54168..d716fc6 100644 --- a/azuread/table_azuread_conditional_access_named_location.go +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -161,19 +161,3 @@ func buildConditionalAccessNamedLocationQueryFilter(equalQuals plugin.KeyColumnE return filters } - -//// TRANSFORM FUNCTIONS - -func adConditionalAccessNamedLocationTitle(_ context.Context, d *transform.TransformData) (interface{}, error) { - data := d.HydrateItem.(*ADLocationInfo) - if data == nil { - return nil, nil - } - - title := data.GetDisplayName() - if title == nil { - title = data.GetId() - } - - return title, nil -} diff --git a/azuread/transforms.go b/azuread/transforms.go index 9c56ee1..1ff2174 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -575,7 +575,7 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf IPv4CidrArr := []map[string]interface{}{} IPv4RangeArr := []map[string]interface{}{} IPv6CidrArr := []map[string]interface{}{} - IPv6RangeArr := m[]map[string]interface{}{} + IPv6RangeArr := []map[string]interface{}{} for i := 0; i < len(ipRangesArray); i++ { switch t := ipRangesArray[i].(type) { diff --git a/docs/tables/azuread_conditional_access_named_location.md b/docs/tables/azuread_conditional_access_named_location.md new file mode 100644 index 0000000..e835574 --- /dev/null +++ b/docs/tables/azuread_conditional_access_named_location.md @@ -0,0 +1,62 @@ +--- +title: "Steampipe Table: azuread_conditional_access_named_location - Query Microsoft Entra Named Locations using SQL" +description: "Allows users to query Microsoft Entra Named Locations, providing information about custom definitions of Named Locations" +--- + +# Table: azuread_conditional_access_named_location - Query Microsoft Entra Named Locations using SQL + +Microsoft Entra Named Locations is a feature in Azure Active Directory (Microsoft Entra) that allows administrators to define custom Named Locations. These Custom named locations can be included in Conditional Access Policies and restrict user access to this specific locations. There are two types of Named Locations - IP based Named locations and Country based Named Locations, the table supports both types. + +## Table Usage Guide + +The `azuread_conditional_access_named_location` table provides insights into Named Locations within Azure Active Directory (Microsoft Entra). As a security administrator, you can understand policies based on Named Locations better through this table, including display name, type, and detailed location information. Utilize it to uncover information about custom Named Locations, understand Conditional Access policies better, and maintain security and compliance within your organization. + +## Examples + +### Basic info +Analyze the settings to understand the status and creation date of the Named Locations in your Microsoft Entra Named Locations. This can help you assess the locations elements within your Conditional Access Policy and make necessary adjustments. + +```sql+postgres +select + id, + display_name, + type, + created_date_time, + modified_date_time +from + azuread_conditional_access_named_location; +``` + +```sql+sqlite +select + id, + display_name, + type, + created_date_time, + modified_date_time +from + azuread_conditional_access_named_location; +``` + +### Detailed information about the Namedl Location definitions +Analyze detailed information about the definition of Named Locations in your Microsoft Entra Named Locations. This can help you understand the locations elements within your Conditional Access Policy and assure the definitions are compliance within your organization policies. + +```sql+postgres +select + id, + display_name, + type, + location_info +from + azuread_conditional_access_named_location; +``` + +```sql+sqlite +select + id, + display_name, + type, + location_info +from + azuread_conditional_access_named_location; +``` \ No newline at end of file From 18e61adbb96d9a5bcce18e895dd08f5d63285f6e Mon Sep 17 00:00:00 2001 From: House Mouse Date: Thu, 8 Aug 2024 11:28:51 +0300 Subject: [PATCH 13/15] fixed new problem with location_info --- azuread/transforms.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azuread/transforms.go b/azuread/transforms.go index 1ff2174..e1ef837 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -582,21 +582,21 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf case *models.IPv4CidrRange: IPv4CidrPair := map[string]interface{}{} IPv4CidrPair["Address"] = *t.GetCidrAddress() - IPv4CidrArr[i] = IPv4CidrPair + IPv4CidrArr = append(IPv4CidrArr, IPv4CidrPair) case *models.IPv4Range: IPv4AddressPair := map[string]interface{}{} IPv4AddressPair["Lower"] = *t.GetLowerAddress() IPv4AddressPair["Upper"] = *t.GetUpperAddress() - IPv4RangeArr[i] = IPv4AddressPair + IPv4RangeArr = append(IPv4RangeArr, IPv4AddressPair) case *models.IPv6CidrRange: IPv6CidrPair := map[string]interface{}{} IPv6CidrPair["Address"] = *t.GetCidrAddress() - IPv6CidrArr[i] = IPv6CidrPair + IPv6CidrArr = append(IPv6CidrArr, IPv6CidrPair) case *models.IPv6Range: IPv6AddressPair := map[string]interface{}{} IPv6AddressPair["Lower"] = *t.GetLowerAddress() IPv6AddressPair["Upper"] = *t.GetUpperAddress() - IPv6RangeArr[i] = IPv6AddressPair + IPv6RangeArr = append(IPv6RangeArr, IPv6AddressPair) } } From 068f94c7df8d7df56d4a6b45f1dadd58c11f9c67 Mon Sep 17 00:00:00 2001 From: House Mouse Date: Thu, 22 Aug 2024 15:45:29 +0300 Subject: [PATCH 14/15] fixed comments from pull request, Used StreamList to structure instead of using response type, changed type column to location_type --- azuread/plugin.go | 2 +- ...uread_conditional_access_named_location.go | 60 ++++++++++++++----- azuread/transforms.go | 30 +++++++--- ...uread_conditional_access_named_location.md | 34 +++++++++-- 4 files changed, 94 insertions(+), 32 deletions(-) diff --git a/azuread/plugin.go b/azuread/plugin.go index 2f70c7c..b5978a1 100644 --- a/azuread/plugin.go +++ b/azuread/plugin.go @@ -33,8 +33,8 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azuread_application": tableAzureAdApplication(ctx), "azuread_application_app_role_assigned_to": tableAzureAdApplicationAppRoleAssignment(ctx), "azuread_authorization_policy": tableAzureAdAuthorizationPolicy(ctx), - "azuread_conditional_access_policy": tableAzureAdConditionalAccessPolicy(ctx), "azuread_conditional_access_named_location": tableAzureAdConditionalAccessNamedLocation(ctx), + "azuread_conditional_access_policy": tableAzureAdConditionalAccessPolicy(ctx), "azuread_device": tableAzureAdDevice(ctx), "azuread_directory_audit_report": tableAzureAdDirectoryAuditReport(ctx), "azuread_directory_role": tableAzureAdDirectoryRole(ctx), diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go index d716fc6..3a686a8 100644 --- a/azuread/table_azuread_conditional_access_named_location.go +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -36,16 +36,21 @@ func tableAzureAdConditionalAccessNamedLocation(_ context.Context) *plugin.Table }, KeyColumns: []*plugin.KeyColumn{ {Name: "display_name", Require: plugin.Optional}, + {Name: "id", Require: plugin.Optional}, + {Name: "location_type", Require: plugin.Optional}, }, }, Columns: commonColumns([]*plugin.Column{ {Name: "id", Type: proto.ColumnType_STRING, Description: "Specifies the identifier of a Named Location object.", Transform: transform.FromMethod("GetId")}, {Name: "display_name", Type: proto.ColumnType_STRING, Description: "Specifies a display name for the Named Location object.", Transform: transform.FromMethod("GetDisplayName")}, - {Name: "location_info", Type: proto.ColumnType_JSON, Description: "Specifies some location information for the Named Location object. Now supported: IP (v4/6 and CIDR/Range), odata_type, IsTrusted (for IP named locations only). Country (and regions, if exist), lookup method, UnkownCountriesAndRegions (for country named locations only)", Transform: transform.FromMethod("GetLocationInfo")}, - {Name: "type", Type: proto.ColumnType_STRING, Description: "Specifies the type of the Named Location object: IP or Country", Transform: transform.FromMethod("GetType")}, + {Name: "location_type", Type: proto.ColumnType_STRING, Description: "Specifies the type of the Named Location object: IP or Country", Transform: transform.FromMethod("GetType")}, {Name: "created_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The create date of the Named Location object.", Transform: transform.FromMethod("GetCreatedDateTime")}, {Name: "modified_date_time", Type: proto.ColumnType_TIMESTAMP, Description: "The modification date of Named Location object.", Transform: transform.FromMethod("GetModifiedDateTime")}, + {Name: "location_info", Type: proto.ColumnType_JSON, Description: "Specifies some location information for the Named Location object. Now supported: IP (v4/6 and CIDR/Range), odata_type, IsTrusted (for IP named locations only). Country (and regions, if exist), lookup method, UnkownCountriesAndRegions (for country named locations only)", Transform: transform.FromMethod("GetLocationInfo")}, + + // Standard columns +{Name: "title", Type: proto.ColumnType_STRING, Description: ColumnDescriptionTitle, Transform: transform.FromMethod("GetDisplayName")}, }), } } @@ -88,29 +93,30 @@ func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryD result, err := client.Identity().ConditionalAccess().NamedLocations().Get(ctx, options) if err != nil { errObj := getErrorObject(err) - plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "list_conditional_access_named_location_error", errObj) + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.listAdConditionalAccessNamedLocations", "list_conditional_access_named_location_error", errObj) return nil, errObj } pageIterator, err := msgraphcore.NewPageIterator[models.NamedLocationable](result, adapter, models.CreateNamedLocationCollectionResponseFromDiscriminatorValue) if err != nil { - plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "create_iterator_instance_error", err) + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.listAdConditionalAccessNamedLocations", "create_iterator_instance_error", err) return nil, err } err = pageIterator.Iterate(ctx, func(pageItem models.NamedLocationable) bool { - switch t := pageItem.(type) { - case *models.IpNamedLocation: - d.StreamListItem(ctx, &ADIpNamedLocationInfo{t}) - case *models.CountryNamedLocation: - d.StreamListItem(ctx, &ADCountryNamedLocationInfo{t}) - } - + + + d.StreamListItem(ctx, ADNamedLocationInfo{ + NamedLocationable: pageItem, + detailedNamedLocation: getNamedLocationDetails(pageItem), + }) + + // Context can be cancelled due to manual cancellation or the limit has been hit return d.RowsRemaining(ctx) != 0 }) if err != nil { - plugin.Logger(ctx).Error("listAdConditionalAccessNamedLocations", "paging_error", err) + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.listAdConditionalAccessNamedLocations", "paging_error", err) return nil, err } @@ -136,10 +142,14 @@ func getAdConditionalAccessNamedLocation(ctx context.Context, d *plugin.QueryDat location, err := client.Identity().ConditionalAccess().NamedLocations().ByNamedLocationId(conditionalAccessNamedLocationId).Get(ctx, nil) if err != nil { errObj := getErrorObject(err) - plugin.Logger(ctx).Error("getAdConditionalAccessNamedLocation", "get_conditional_access_location_error", errObj) + plugin.Logger(ctx).Error("azuread_conditional_access_named_location.getAdConditionalAccessNamedLocation", "get_conditional_access_location_error", errObj) return nil, errObj } - return &ADLocationInfo{location}, nil + + return &ADNamedLocationInfo{ + NamedLocationable: location, + detailedNamedLocation: getNamedLocationDetails(location), + } , nil } func buildConditionalAccessNamedLocationQueryFilter(equalQuals plugin.KeyColumnEqualsQualMap) []string { @@ -147,17 +157,35 @@ func buildConditionalAccessNamedLocationQueryFilter(equalQuals plugin.KeyColumnE filterQuals := map[string]string{ "display_name": "string", - "state": "string", + "id": "string", } for qual, qualType := range filterQuals { switch qualType { case "string": if equalQuals[qual] != nil { - filters = append(filters, fmt.Sprintf("%s eq '%s'", strcase.ToCamel(qual), equalQuals[qual].GetStringValue())) + if qual == "location_type" { + filters = append(filters, fmt.Sprintf("type eq '%s'", equalQuals[qual].GetStringValue())) + } else { + filters = append(filters, fmt.Sprintf("%s eq '%s'", strcase.ToLowerCamel(qual), equalQuals[qual].GetStringValue())) + } } } } return filters } + +/// UTILITY FUNCTION + +func getNamedLocationDetails(i interface{}) models.NamedLocationable { + + switch t := i.(type) { + case *models.IpNamedLocation: + return ADIpNamedLocationInfo{t} + case *models.CountryNamedLocation: + return ADCountryNamedLocationInfo{t} + } + + return nil +} \ No newline at end of file diff --git a/azuread/transforms.go b/azuread/transforms.go index e1ef837..0896167 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -59,8 +59,9 @@ type ADIdentityProviderInfo struct { ClientSecret interface{} } -type ADLocationInfo struct { +type ADNamedLocationInfo struct { models.NamedLocationable + detailedNamedLocation models.NamedLocationable } type ADIpNamedLocationInfo struct { @@ -568,7 +569,7 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } -func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interface{} { +func IpGetLocationInfo(ipLocationInfo *ADIpNamedLocationInfo) map[string]interface{} { ipRangesArray := ipLocationInfo.GetIpRanges() locationInfoJSON := map[string]interface{}{} @@ -600,7 +601,6 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf } } - locationInfoJSON["type"] = "IP" locationInfoJSON["IPv4Cidr"] = IPv4CidrArr locationInfoJSON["IPv4Range"] = IPv4RangeArr locationInfoJSON["IPv6Cidr"] = IPv6CidrArr @@ -609,23 +609,35 @@ func (ipLocationInfo *ADIpNamedLocationInfo) GetLocationInfo() map[string]interf return locationInfoJSON } -func (countryLocationInfo *ADCountryNamedLocationInfo) GetLocationInfo() map[string]interface{} { +func CountryGetLocationInfo(countryLocationInfo *ADCountryNamedLocationInfo) map[string]interface{} { locationInfoJSON := map[string]interface{}{} - locationInfoJSON["type"] = "Country" locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() locationInfoJSON["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() return locationInfoJSON } -func (ipLocationInfo *ADIpNamedLocationInfo) GetType() string { - return "IP" +func (locationInfo *ADNamedLocationInfo) GetLocationInfo() map[string]interface{} { + switch t := locationInfo.detailedNamedLocation.(type) { + case ADIpNamedLocationInfo: + return IpGetLocationInfo(&ADIpNamedLocationInfo{t}) + case ADCountryNamedLocationInfo: + return CountryGetLocationInfo(&ADCountryNamedLocationInfo{t}) + } + return nil } -func (countryLocationInfo *ADCountryNamedLocationInfo) GetType() string { - return "Country" +func (locationInfo *ADNamedLocationInfo) GetType() string { + switch locationInfo.detailedNamedLocation.(type) { + case ADIpNamedLocationInfo: + return "IP" + case ADCountryNamedLocationInfo: + return "Country" + } + return "Unkown" } + func (directoryAuditReport *ADDirectoryAuditReportInfo) DirectoryAuditAdditionalDetails() []map[string]interface{} { if directoryAuditReport.GetAdditionalDetails() == nil { return nil diff --git a/docs/tables/azuread_conditional_access_named_location.md b/docs/tables/azuread_conditional_access_named_location.md index e835574..31301eb 100644 --- a/docs/tables/azuread_conditional_access_named_location.md +++ b/docs/tables/azuread_conditional_access_named_location.md @@ -20,7 +20,7 @@ Analyze the settings to understand the status and creation date of the Named Loc select id, display_name, - type, + location_type, created_date_time, modified_date_time from @@ -31,21 +31,21 @@ from select id, display_name, - type, + location_type, created_date_time, modified_date_time from azuread_conditional_access_named_location; ``` -### Detailed information about the Namedl Location definitions +### Detailed information about the Named Location definitions Analyze detailed information about the definition of Named Locations in your Microsoft Entra Named Locations. This can help you understand the locations elements within your Conditional Access Policy and assure the definitions are compliance within your organization policies. ```sql+postgres select id, display_name, - type, + location_type, location_info from azuread_conditional_access_named_location; @@ -55,8 +55,30 @@ from select id, display_name, - type, + location_type, location_info from azuread_conditional_access_named_location; -``` \ No newline at end of file +``` + +### Detailed information about IP based named location +Retrieve IP based Named Locations in your Microsoft Entra Named Locations. This can help you understand the locations elements within your Conditional Access Policy distringuishes between different types of named locations (Options: [IP, Country]). + +```sql+postgres +select + id, + display_name, + location_info +from + azuread_conditional_access_named_location where location_type = 'IP'; +``` + +```sql+sqlite +select + id, + display_name, + location_info +from + azuread_conditional_access_named_location where location_type = 'IP'; +``` + From c73695f32aa0b314cc4f4d6f37b09574722d9e94 Mon Sep 17 00:00:00 2001 From: House Mouse Date: Mon, 26 Aug 2024 11:14:20 +0300 Subject: [PATCH 15/15] changed transform function info location table --- ...uread_conditional_access_named_location.go | 74 ++++++++++++++++++- azuread/transforms.go | 71 +----------------- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/azuread/table_azuread_conditional_access_named_location.go b/azuread/table_azuread_conditional_access_named_location.go index 3a686a8..11bbe71 100644 --- a/azuread/table_azuread_conditional_access_named_location.go +++ b/azuread/table_azuread_conditional_access_named_location.go @@ -108,7 +108,7 @@ func listAdConditionalAccessNamedLocations(ctx context.Context, d *plugin.QueryD d.StreamListItem(ctx, ADNamedLocationInfo{ NamedLocationable: pageItem, - detailedNamedLocation: getNamedLocationDetails(pageItem), + NamedLocation: getNamedLocationDetails(pageItem), }) @@ -148,7 +148,7 @@ func getAdConditionalAccessNamedLocation(ctx context.Context, d *plugin.QueryDat return &ADNamedLocationInfo{ NamedLocationable: location, - detailedNamedLocation: getNamedLocationDetails(location), + NamedLocation: getNamedLocationDetails(location), } , nil } @@ -188,4 +188,74 @@ func getNamedLocationDetails(i interface{}) models.NamedLocationable { } return nil +} + +//// TRANSFORM FUNCTIONS + +func IpGetLocationInfo(ipLocationInfo *ADIpNamedLocationInfo) map[string]interface{} { + ipRangesArray := ipLocationInfo.GetIpRanges() + locationInfoJSON := map[string]interface{}{} + + IPv4CidrArr := []map[string]interface{}{} + IPv4RangeArr := []map[string]interface{}{} + IPv6CidrArr := []map[string]interface{}{} + IPv6RangeArr := []map[string]interface{}{} + + for i := 0; i < len(ipRangesArray); i++ { + switch t := ipRangesArray[i].(type) { + case *models.IPv4CidrRange: + IPv4CidrPair := map[string]interface{}{} + IPv4CidrPair["Address"] = *t.GetCidrAddress() + IPv4CidrArr = append(IPv4CidrArr, IPv4CidrPair) + case *models.IPv4Range: + IPv4AddressPair := map[string]interface{}{} + IPv4AddressPair["Lower"] = *t.GetLowerAddress() + IPv4AddressPair["Upper"] = *t.GetUpperAddress() + IPv4RangeArr = append(IPv4RangeArr, IPv4AddressPair) + case *models.IPv6CidrRange: + IPv6CidrPair := map[string]interface{}{} + IPv6CidrPair["Address"] = *t.GetCidrAddress() + IPv6CidrArr = append(IPv6CidrArr, IPv6CidrPair) + case *models.IPv6Range: + IPv6AddressPair := map[string]interface{}{} + IPv6AddressPair["Lower"] = *t.GetLowerAddress() + IPv6AddressPair["Upper"] = *t.GetUpperAddress() + IPv6RangeArr = append(IPv6RangeArr, IPv6AddressPair) + } + } + + locationInfoJSON["IPv4Cidr"] = IPv4CidrArr + locationInfoJSON["IPv4Range"] = IPv4RangeArr + locationInfoJSON["IPv6Cidr"] = IPv6CidrArr + locationInfoJSON["IPv6Range"] = IPv6RangeArr + locationInfoJSON["IsTrusted"] = ipLocationInfo.GetIsTrusted() + return locationInfoJSON +} + +func CountryGetLocationInfo(countryLocationInfo *ADCountryNamedLocationInfo) map[string]interface{} { + locationInfoJSON := map[string]interface{}{} + locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() + locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() + locationInfoJSON["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() + return locationInfoJSON +} + +func (locationInfo *ADNamedLocationInfo) GetLocationInfo() map[string]interface{} { + switch t := locationInfo.NamedLocation.(type) { + case ADIpNamedLocationInfo: + return IpGetLocationInfo(&ADIpNamedLocationInfo{t}) + case ADCountryNamedLocationInfo: + return CountryGetLocationInfo(&ADCountryNamedLocationInfo{t}) + } + return nil +} + +func (locationInfo *ADNamedLocationInfo) GetType() string { + switch locationInfo.NamedLocation.(type) { + case ADIpNamedLocationInfo: + return "IP" + case ADCountryNamedLocationInfo: + return "Country" + } + return "Unkown" } \ No newline at end of file diff --git a/azuread/transforms.go b/azuread/transforms.go index 0896167..859c37d 100644 --- a/azuread/transforms.go +++ b/azuread/transforms.go @@ -61,7 +61,7 @@ type ADIdentityProviderInfo struct { type ADNamedLocationInfo struct { models.NamedLocationable - detailedNamedLocation models.NamedLocationable + NamedLocation models.NamedLocationable } type ADIpNamedLocationInfo struct { @@ -569,75 +569,6 @@ func (device *ADDeviceInfo) DeviceMemberOf() []map[string]interface{} { return members } -func IpGetLocationInfo(ipLocationInfo *ADIpNamedLocationInfo) map[string]interface{} { - ipRangesArray := ipLocationInfo.GetIpRanges() - locationInfoJSON := map[string]interface{}{} - - IPv4CidrArr := []map[string]interface{}{} - IPv4RangeArr := []map[string]interface{}{} - IPv6CidrArr := []map[string]interface{}{} - IPv6RangeArr := []map[string]interface{}{} - - for i := 0; i < len(ipRangesArray); i++ { - switch t := ipRangesArray[i].(type) { - case *models.IPv4CidrRange: - IPv4CidrPair := map[string]interface{}{} - IPv4CidrPair["Address"] = *t.GetCidrAddress() - IPv4CidrArr = append(IPv4CidrArr, IPv4CidrPair) - case *models.IPv4Range: - IPv4AddressPair := map[string]interface{}{} - IPv4AddressPair["Lower"] = *t.GetLowerAddress() - IPv4AddressPair["Upper"] = *t.GetUpperAddress() - IPv4RangeArr = append(IPv4RangeArr, IPv4AddressPair) - case *models.IPv6CidrRange: - IPv6CidrPair := map[string]interface{}{} - IPv6CidrPair["Address"] = *t.GetCidrAddress() - IPv6CidrArr = append(IPv6CidrArr, IPv6CidrPair) - case *models.IPv6Range: - IPv6AddressPair := map[string]interface{}{} - IPv6AddressPair["Lower"] = *t.GetLowerAddress() - IPv6AddressPair["Upper"] = *t.GetUpperAddress() - IPv6RangeArr = append(IPv6RangeArr, IPv6AddressPair) - } - } - - locationInfoJSON["IPv4Cidr"] = IPv4CidrArr - locationInfoJSON["IPv4Range"] = IPv4RangeArr - locationInfoJSON["IPv6Cidr"] = IPv6CidrArr - locationInfoJSON["IPv6Range"] = IPv6RangeArr - locationInfoJSON["IsTrusted"] = ipLocationInfo.GetIsTrusted() - return locationInfoJSON -} - -func CountryGetLocationInfo(countryLocationInfo *ADCountryNamedLocationInfo) map[string]interface{} { - locationInfoJSON := map[string]interface{}{} - locationInfoJSON["Countries_and_Regions"] = countryLocationInfo.GetCountriesAndRegions() - locationInfoJSON["Get_Unknown_Countries_and_Regions"] = countryLocationInfo.GetIncludeUnknownCountriesAndRegions() - locationInfoJSON["Lookup_Method"] = countryLocationInfo.GetCountryLookupMethod().String() - return locationInfoJSON -} - -func (locationInfo *ADNamedLocationInfo) GetLocationInfo() map[string]interface{} { - switch t := locationInfo.detailedNamedLocation.(type) { - case ADIpNamedLocationInfo: - return IpGetLocationInfo(&ADIpNamedLocationInfo{t}) - case ADCountryNamedLocationInfo: - return CountryGetLocationInfo(&ADCountryNamedLocationInfo{t}) - } - return nil -} - -func (locationInfo *ADNamedLocationInfo) GetType() string { - switch locationInfo.detailedNamedLocation.(type) { - case ADIpNamedLocationInfo: - return "IP" - case ADCountryNamedLocationInfo: - return "Country" - } - return "Unkown" -} - - func (directoryAuditReport *ADDirectoryAuditReportInfo) DirectoryAuditAdditionalDetails() []map[string]interface{} { if directoryAuditReport.GetAdditionalDetails() == nil { return nil