From 7748bcf3fe4cf189f0b40a8f4b76e44c8990ea4e Mon Sep 17 00:00:00 2001 From: Hossein Rouhani <56231339+HRouhani@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:01:08 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20Adding=20Country=20location=20to?= =?UTF-8?q?=20NamedLocation=20under=20conditional=20access=20-=20MS365=20(?= =?UTF-8?q?#4848)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🧹 Adding Country location to NamedLocation under confitional access - MS365 Signed-off-by: Hossein Rouhani * improving the resources Signed-off-by: Hossein Rouhani * improving the errors Signed-off-by: Hossein Rouhani * rebase Signed-off-by: Hossein Rouhani --------- Signed-off-by: Hossein Rouhani --- .../ms365/resources/conditional-access.go | 52 +++++- providers/ms365/resources/ms365.lr | 20 +- providers/ms365/resources/ms365.lr.go | 173 +++++++++++++++++- .../ms365/resources/ms365.lr.manifest.yaml | 13 +- 4 files changed, 243 insertions(+), 15 deletions(-) diff --git a/providers/ms365/resources/conditional-access.go b/providers/ms365/resources/conditional-access.go index 773f4d2c7..a8bbaf8fb 100644 --- a/providers/ms365/resources/conditional-access.go +++ b/providers/ms365/resources/conditional-access.go @@ -15,7 +15,7 @@ func (m *mqlMicrosoftConditionalAccessIpNamedLocation) id() (string, error) { return m.Name.Data, nil } -func (a *mqlMicrosoftConditionalAccess) namedLocations() ([]interface{}, error) { +func (a *mqlMicrosoftConditionalAccessNamedLocations) ipLocations() ([]interface{}, error) { conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) graphClient, err := conn.GraphClient() if err != nil { @@ -59,3 +59,53 @@ func (a *mqlMicrosoftConditionalAccess) namedLocations() ([]interface{}, error) return locationDetails, nil } + +func (m *mqlMicrosoftConditionalAccessCountryNamedLocation) id() (string, error) { + return m.Name.Data, nil +} + +func (a *mqlMicrosoftConditionalAccessNamedLocations) countryLocations() ([]interface{}, error) { + conn := a.MqlRuntime.Connection.(*connection.Ms365Connection) + graphClient, err := conn.GraphClient() + if err != nil { + return nil, err + } + + ctx := context.Background() + namedLocations, err := graphClient.Identity().ConditionalAccess().NamedLocations().Get(ctx, nil) + if err != nil { + return nil, transformError(err) + } + + var locationDetails []interface{} + for _, location := range namedLocations.GetValue() { + if countryLocation, ok := location.(*models.CountryNamedLocation); ok { + displayName := countryLocation.GetDisplayName() + countryLookupMethod := countryLocation.GetCountryLookupMethod() + + var lookupMethodStr *string + if countryLookupMethod != nil { + method := countryLookupMethod.String() + lookupMethodStr = &method + } + + if displayName != nil && lookupMethodStr != nil { + locationInfo, err := CreateResource(a.MqlRuntime, "microsoft.conditionalAccess.countryNamedLocation", + map[string]*llx.RawData{ + "name": llx.StringDataPtr(displayName), + "lookupMethod": llx.StringDataPtr(lookupMethodStr), + }) + if err != nil { + return nil, err + } + locationDetails = append(locationDetails, locationInfo) + } + } + } + + if len(locationDetails) == 0 { + return nil, nil + } + + return locationDetails, nil +} diff --git a/providers/ms365/resources/ms365.lr b/providers/ms365/resources/ms365.lr index a5c6ead4f..63b2a468a 100644 --- a/providers/ms365/resources/ms365.lr +++ b/providers/ms365/resources/ms365.lr @@ -58,8 +58,16 @@ microsoft.tenant @defaults("name") { // Microsoft Conditional Access Policies microsoft.conditionalAccess { - // IP named location - namedLocations() []microsoft.conditionalAccess.ipNamedLocation + // Named locations container + namedLocations microsoft.conditionalAccess.namedLocations +} + +// Container for Microsoft Conditional Access Named Locations +microsoft.conditionalAccess.namedLocations { + // IP-based named locations + ipLocations() []microsoft.conditionalAccess.ipNamedLocation + // Country-based named locations + countryLocations() []microsoft.conditionalAccess.countryNamedLocation } // Microsoft Conditional Access IP named location @@ -70,6 +78,14 @@ microsoft.conditionalAccess.ipNamedLocation @defaults("name trusted") { trusted bool } +// Microsoft Conditional Access Country named location +microsoft.conditionalAccess.countryNamedLocation @defaults("name lookupMethod") { + // Named location name + name string + // Method to determine the country location + lookupMethod string +} + // Microsoft Entra ID user private microsoft.user @defaults("id displayName userPrincipalName") { // User Object ID diff --git a/providers/ms365/resources/ms365.lr.go b/providers/ms365/resources/ms365.lr.go index d0d7f8b45..9755e5006 100644 --- a/providers/ms365/resources/ms365.lr.go +++ b/providers/ms365/resources/ms365.lr.go @@ -30,10 +30,18 @@ func init() { // to override args, implement: initMicrosoftConditionalAccess(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createMicrosoftConditionalAccess, }, + "microsoft.conditionalAccess.namedLocations": { + // to override args, implement: initMicrosoftConditionalAccessNamedLocations(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createMicrosoftConditionalAccessNamedLocations, + }, "microsoft.conditionalAccess.ipNamedLocation": { // to override args, implement: initMicrosoftConditionalAccessIpNamedLocation(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createMicrosoftConditionalAccessIpNamedLocation, }, + "microsoft.conditionalAccess.countryNamedLocation": { + // to override args, implement: initMicrosoftConditionalAccessCountryNamedLocation(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createMicrosoftConditionalAccessCountryNamedLocation, + }, "microsoft.user": { Init: initMicrosoftUser, Create: createMicrosoftUser, @@ -298,7 +306,13 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ return (r.(*mqlMicrosoftTenant).GetSubscriptions()).ToDataRes(types.Array(types.Dict)) }, "microsoft.conditionalAccess.namedLocations": func(r plugin.Resource) *plugin.DataRes { - return (r.(*mqlMicrosoftConditionalAccess).GetNamedLocations()).ToDataRes(types.Array(types.Resource("microsoft.conditionalAccess.ipNamedLocation"))) + return (r.(*mqlMicrosoftConditionalAccess).GetNamedLocations()).ToDataRes(types.Resource("microsoft.conditionalAccess.namedLocations")) + }, + "microsoft.conditionalAccess.namedLocations.ipLocations": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftConditionalAccessNamedLocations).GetIpLocations()).ToDataRes(types.Array(types.Resource("microsoft.conditionalAccess.ipNamedLocation"))) + }, + "microsoft.conditionalAccess.namedLocations.countryLocations": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftConditionalAccessNamedLocations).GetCountryLocations()).ToDataRes(types.Array(types.Resource("microsoft.conditionalAccess.countryNamedLocation"))) }, "microsoft.conditionalAccess.ipNamedLocation.name": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftConditionalAccessIpNamedLocation).GetName()).ToDataRes(types.String) @@ -306,6 +320,12 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "microsoft.conditionalAccess.ipNamedLocation.trusted": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftConditionalAccessIpNamedLocation).GetTrusted()).ToDataRes(types.Bool) }, + "microsoft.conditionalAccess.countryNamedLocation.name": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftConditionalAccessCountryNamedLocation).GetName()).ToDataRes(types.String) + }, + "microsoft.conditionalAccess.countryNamedLocation.lookupMethod": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftConditionalAccessCountryNamedLocation).GetLookupMethod()).ToDataRes(types.String) + }, "microsoft.user.id": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftUser).GetId()).ToDataRes(types.String) }, @@ -1261,7 +1281,19 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { return }, "microsoft.conditionalAccess.namedLocations": func(r plugin.Resource, v *llx.RawData) (ok bool) { - r.(*mqlMicrosoftConditionalAccess).NamedLocations, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + r.(*mqlMicrosoftConditionalAccess).NamedLocations, ok = plugin.RawToTValue[*mqlMicrosoftConditionalAccessNamedLocations](v.Value, v.Error) + return + }, + "microsoft.conditionalAccess.namedLocations.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessNamedLocations).__id, ok = v.Value.(string) + return + }, + "microsoft.conditionalAccess.namedLocations.ipLocations": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessNamedLocations).IpLocations, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "microsoft.conditionalAccess.namedLocations.countryLocations": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessNamedLocations).CountryLocations, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, "microsoft.conditionalAccess.ipNamedLocation.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { @@ -1276,6 +1308,18 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlMicrosoftConditionalAccessIpNamedLocation).Trusted, ok = plugin.RawToTValue[bool](v.Value, v.Error) return }, + "microsoft.conditionalAccess.countryNamedLocation.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessCountryNamedLocation).__id, ok = v.Value.(string) + return + }, + "microsoft.conditionalAccess.countryNamedLocation.name": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessCountryNamedLocation).Name, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.conditionalAccess.countryNamedLocation.lookupMethod": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftConditionalAccessCountryNamedLocation).LookupMethod, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, "microsoft.user.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlMicrosoftUser).__id, ok = v.Value.(string) return @@ -2855,7 +2899,7 @@ type mqlMicrosoftConditionalAccess struct { MqlRuntime *plugin.Runtime __id string // optional: if you define mqlMicrosoftConditionalAccessInternal it will be used here - NamedLocations plugin.TValue[[]interface{}] + NamedLocations plugin.TValue[*mqlMicrosoftConditionalAccessNamedLocations] } // createMicrosoftConditionalAccess creates a new instance of this resource @@ -2890,10 +2934,55 @@ func (c *mqlMicrosoftConditionalAccess) MqlID() string { return c.__id } -func (c *mqlMicrosoftConditionalAccess) GetNamedLocations() *plugin.TValue[[]interface{}] { - return plugin.GetOrCompute[[]interface{}](&c.NamedLocations, func() ([]interface{}, error) { +func (c *mqlMicrosoftConditionalAccess) GetNamedLocations() *plugin.TValue[*mqlMicrosoftConditionalAccessNamedLocations] { + return &c.NamedLocations +} + +// mqlMicrosoftConditionalAccessNamedLocations for the microsoft.conditionalAccess.namedLocations resource +type mqlMicrosoftConditionalAccessNamedLocations struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMicrosoftConditionalAccessNamedLocationsInternal it will be used here + IpLocations plugin.TValue[[]interface{}] + CountryLocations plugin.TValue[[]interface{}] +} + +// createMicrosoftConditionalAccessNamedLocations creates a new instance of this resource +func createMicrosoftConditionalAccessNamedLocations(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftConditionalAccessNamedLocations{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + // to override __id implement: id() (string, error) + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("microsoft.conditionalAccess.namedLocations", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMicrosoftConditionalAccessNamedLocations) MqlName() string { + return "microsoft.conditionalAccess.namedLocations" +} + +func (c *mqlMicrosoftConditionalAccessNamedLocations) MqlID() string { + return c.__id +} + +func (c *mqlMicrosoftConditionalAccessNamedLocations) GetIpLocations() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.IpLocations, func() ([]interface{}, error) { if c.MqlRuntime.HasRecording { - d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.conditionalAccess", c.__id, "namedLocations") + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.conditionalAccess.namedLocations", c.__id, "ipLocations") if err != nil { return nil, err } @@ -2902,7 +2991,23 @@ func (c *mqlMicrosoftConditionalAccess) GetNamedLocations() *plugin.TValue[[]int } } - return c.namedLocations() + return c.ipLocations() + }) +} + +func (c *mqlMicrosoftConditionalAccessNamedLocations) GetCountryLocations() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.CountryLocations, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.conditionalAccess.namedLocations", c.__id, "countryLocations") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.countryLocations() }) } @@ -2960,6 +3065,60 @@ func (c *mqlMicrosoftConditionalAccessIpNamedLocation) GetTrusted() *plugin.TVal return &c.Trusted } +// mqlMicrosoftConditionalAccessCountryNamedLocation for the microsoft.conditionalAccess.countryNamedLocation resource +type mqlMicrosoftConditionalAccessCountryNamedLocation struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMicrosoftConditionalAccessCountryNamedLocationInternal it will be used here + Name plugin.TValue[string] + LookupMethod plugin.TValue[string] +} + +// createMicrosoftConditionalAccessCountryNamedLocation creates a new instance of this resource +func createMicrosoftConditionalAccessCountryNamedLocation(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftConditionalAccessCountryNamedLocation{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + if res.__id == "" { + res.__id, err = res.id() + if err != nil { + return nil, err + } + } + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("microsoft.conditionalAccess.countryNamedLocation", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMicrosoftConditionalAccessCountryNamedLocation) MqlName() string { + return "microsoft.conditionalAccess.countryNamedLocation" +} + +func (c *mqlMicrosoftConditionalAccessCountryNamedLocation) MqlID() string { + return c.__id +} + +func (c *mqlMicrosoftConditionalAccessCountryNamedLocation) GetName() *plugin.TValue[string] { + return &c.Name +} + +func (c *mqlMicrosoftConditionalAccessCountryNamedLocation) GetLookupMethod() *plugin.TValue[string] { + return &c.LookupMethod +} + // mqlMicrosoftUser for the microsoft.user resource type mqlMicrosoftUser struct { MqlRuntime *plugin.Runtime diff --git a/providers/ms365/resources/ms365.lr.manifest.yaml b/providers/ms365/resources/ms365.lr.manifest.yaml index 7e0e9d72d..e88149de7 100755 --- a/providers/ms365/resources/ms365.lr.manifest.yaml +++ b/providers/ms365/resources/ms365.lr.manifest.yaml @@ -122,20 +122,23 @@ resources: min_mondoo_version: 9.0.0 microsoft.conditionalAccess: fields: - name: {} namedLocations: {} - trusted: {} min_mondoo_version: 9.0.0 - microsoft.conditionalAccess.ipNamedLocation: + microsoft.conditionalAccess.countryNamedLocation: fields: + lookupMethod: {} name: {} - trusted: {} min_mondoo_version: 9.0.0 - microsoft.conditionalAccess.namedLocation: + microsoft.conditionalAccess.ipNamedLocation: fields: name: {} trusted: {} min_mondoo_version: 9.0.0 + microsoft.conditionalAccess.namedLocations: + fields: + countryLocations: {} + ipLocations: {} + min_mondoo_version: 9.0.0 microsoft.devicemanagement: fields: deviceCompliancePolicies: {}