Skip to content

Commit

Permalink
✨ Add microsoft.user.mfaEnabled field. (#4792)
Browse files Browse the repository at this point in the history
Signed-off-by: Preslav <[email protected]>
  • Loading branch information
preslavgerchev authored Oct 30, 2024
1 parent cef4367 commit 8e25e5c
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 1 deletion.
17 changes: 17 additions & 0 deletions providers/ms365/connection/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
errors "github.com/cockroachdb/errors"
"github.com/microsoft/kiota-abstractions-go/authentication"
a "github.com/microsoft/kiota-authentication-azure-go"
beta "github.com/microsoftgraph/msgraph-beta-sdk-go"
msgraphsdkgo "github.com/microsoftgraph/msgraph-sdk-go"
)

Expand Down Expand Up @@ -36,6 +37,22 @@ func graphClient(token azcore.TokenCredential) (*msgraphsdkgo.GraphServiceClient
return graphClient, nil
}

func betaGraphClient(token azcore.TokenCredential) (*beta.GraphServiceClient, error) {
providerFunc := func() (authentication.AuthenticationProvider, error) {
return a.NewAzureIdentityAuthenticationProviderWithScopes(token, DefaultMSGraphScopes)
}
adapter, err := newGraphRequestAdapterWithFn(providerFunc)
if err != nil {
return nil, err
}
graphClient := beta.NewGraphServiceClient(adapter)
return graphClient, nil
}

func (conn *Ms365Connection) GraphClient() (*msgraphsdkgo.GraphServiceClient, error) {
return graphClient(conn.Token())
}

func (conn *Ms365Connection) BetaGraphClient() (*beta.GraphServiceClient, error) {
return betaGraphClient(conn.Token())
}
1 change: 1 addition & 0 deletions providers/ms365/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/google/uuid v1.6.0
github.com/microsoft/kiota-abstractions-go v1.7.0
github.com/microsoft/kiota-authentication-azure-go v1.1.0
github.com/microsoftgraph/msgraph-beta-sdk-go v0.111.0
github.com/microsoftgraph/msgraph-sdk-go v1.51.0
github.com/microsoftgraph/msgraph-sdk-go-core v1.2.1
github.com/rs/zerolog v1.33.0
Expand Down
2 changes: 2 additions & 0 deletions providers/ms365/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ github.com/microsoft/kiota-serialization-multipart-go v1.0.0 h1:3O5sb5Zj+moLBiJy
github.com/microsoft/kiota-serialization-multipart-go v1.0.0/go.mod h1:yauLeBTpANk4L03XD985akNysG24SnRJGaveZf+p4so=
github.com/microsoft/kiota-serialization-text-go v1.0.0 h1:XOaRhAXy+g8ZVpcq7x7a0jlETWnWrEum0RhmbYrTFnA=
github.com/microsoft/kiota-serialization-text-go v1.0.0/go.mod h1:sM1/C6ecnQ7IquQOGUrUldaO5wj+9+v7G2W3sQ3fy6M=
github.com/microsoftgraph/msgraph-beta-sdk-go v0.111.0 h1:IhaMYOdiN76xwkYGVAb7QwkOkjSZTrBMwv+5xCpf2sY=
github.com/microsoftgraph/msgraph-beta-sdk-go v0.111.0/go.mod h1:GJhFAbcwHwtiqspxJnurVNr6XZBfVtdccVEC1+6+vjo=
github.com/microsoftgraph/msgraph-sdk-go v1.51.0 h1:IfRY0uVHToT8X9k6Ri19tKdt8hwPomji2yx5YsKoaw4=
github.com/microsoftgraph/msgraph-sdk-go v1.51.0/go.mod h1:MVTeFCCih3qXy9D0q+f4NdOyumFnMZ+Ppcpurgd30TY=
github.com/microsoftgraph/msgraph-sdk-go-core v1.2.1 h1:P1wpmn3xxfPMFJHg+PJPcusErfRkl63h6OdAnpDbkS8=
Expand Down
8 changes: 8 additions & 0 deletions providers/ms365/resources/microsoft.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ import (

var idxUsersById = &sync.RWMutex{}

type mfaResp struct {
// holds the error if that is what the request returned
err error
mfaMap map[string]bool
}

type mqlMicrosoftInternal struct {
permissionIndexer
// index users by id
idxUsersById map[string]*mqlMicrosoftUser
// the response when asking for the user registration details
mfaResp mfaResp
}

// initIndex ensures the user indexes are initialized,
Expand Down
2 changes: 2 additions & 0 deletions providers/ms365/resources/ms365.lr
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ private microsoft.user @defaults("id displayName userPrincipalName") {
contact() dict
// Authentication information
authMethods() microsoft.user.authenticationMethods
// Whether MFA is enabled for the user.
mfaEnabled() bool
}

// Microsoft Entra authentication methods
Expand Down
14 changes: 14 additions & 0 deletions providers/ms365/resources/ms365.lr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions providers/ms365/resources/ms365.lr.manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,8 @@ resources:
min_mondoo_version: 9.0.0
jobTitle: {}
mail: {}
mfaEnabled:
min_mondoo_version: 9.0.0
mobilePhone: {}
officeLocation: {}
otherMails: {}
Expand Down
59 changes: 58 additions & 1 deletion providers/ms365/resources/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import (
"context"
"errors"
"fmt"
"time"

betamodels "github.com/microsoftgraph/msgraph-beta-sdk-go/models"
"github.com/microsoftgraph/msgraph-beta-sdk-go/reports"
betausers "github.com/microsoftgraph/msgraph-beta-sdk-go/users"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/users"
"go.mondoo.com/cnquery/v11/llx"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/plugin"
"go.mondoo.com/cnquery/v11/providers-sdk/v1/util/convert"
"go.mondoo.com/cnquery/v11/providers/ms365/connection"
"go.mondoo.com/cnquery/v11/types"
"time"
)

var userSelectFields = []string{
Expand All @@ -32,6 +36,10 @@ func (a *mqlMicrosoft) users() ([]interface{}, error) {
return nil, err
}

betaClient, err := conn.BetaGraphClient()
if err != nil {
return nil, err
}
// fetch user data
ctx := context.Background()
top := int32(999)
Expand All @@ -51,6 +59,33 @@ func (a *mqlMicrosoft) users() ([]interface{}, error) {
return nil, transformError(err)
}

detailsResp, err := betaClient.Reports().AuthenticationMethods().UserRegistrationDetails().Get(
ctx,
&reports.AuthenticationMethodsUserRegistrationDetailsRequestBuilderGetRequestConfiguration{
QueryParameters: &reports.AuthenticationMethodsUserRegistrationDetailsRequestBuilderGetQueryParameters{
Top: &top,
},
})
// we do not want to fail the user fetching here, this likely means the tenant does not have the right license
if err != nil {
a.mfaResp = mfaResp{err: err}
} else {
userRegistrationDetails, err := iterate[*betamodels.UserRegistrationDetails](ctx, detailsResp, betaClient.GetAdapter(), betausers.CreateDeltaGetResponseFromDiscriminatorValue)
// we do not want to fail the user fetching here, this likely means the tenant does not have the right license
if err != nil {
a.mfaResp = mfaResp{err: err}
} else {
mfaMap := map[string]bool{}
for _, u := range userRegistrationDetails {
if u.GetId() == nil || u.GetIsMfaRegistered() == nil {
continue
}
mfaMap[*u.GetId()] = *u.GetIsMfaRegistered()
}
a.mfaResp = mfaResp{mfaMap: mfaMap}
}
}

// construct the result
res := []interface{}{}
for _, u := range users {
Expand Down Expand Up @@ -172,6 +207,27 @@ var userJobContactFields = []string{
"officeLocation", "streetAddress", "city", "state", "postalCode", "country", "businessPhones", "mobilePhone", "mail", "otherMails", "faxNumber", "mailNickname",
}

func (a *mqlMicrosoftUser) mfaEnabled() (bool, error) {
mql, err := CreateResource(a.MqlRuntime, "microsoft", map[string]*llx.RawData{})
if err != nil {
return false, err
}

microsoft := mql.(*mqlMicrosoft)
if microsoft.mfaResp.mfaMap == nil {
microsoft.mfaResp.mfaMap = make(map[string]bool)
}
if microsoft.mfaResp.err != nil {
a.MfaEnabled.Error = microsoft.mfaResp.err
a.MfaEnabled.State = plugin.StateIsSet
return false, a.MfaEnabled.Error
}

a.MfaEnabled.Data = microsoft.mfaResp.mfaMap[a.Id.Data]
a.MfaEnabled.State = plugin.StateIsSet
return a.MfaEnabled.Data, nil
}

func (a *mqlMicrosoftUser) populateJobContactData() error {
conn := a.MqlRuntime.Connection.(*connection.Ms365Connection)
graphClient, err := conn.GraphClient()
Expand Down Expand Up @@ -348,6 +404,7 @@ func (a *mqlMicrosoftUser) authMethods() (*mqlMicrosoftUserAuthenticationMethods
}

ctx := context.Background()

authMethods, err := graphClient.Users().ByUserId(userID).Authentication().Methods().Get(ctx, &users.ItemAuthenticationMethodsRequestBuilderGetRequestConfiguration{})
if oErr, ok := isOdataError(err); ok {
if oErr.ResponseStatusCode == 403 {
Expand Down

0 comments on commit 8e25e5c

Please sign in to comment.