From 87da88767af181a27e68d7596632a7d7f1204e4c Mon Sep 17 00:00:00 2001 From: Preslav Date: Tue, 3 Dec 2024 13:48:35 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20audit=20log=20for=20user,=20a?= =?UTF-8?q?dd=20creation=20type=20and=20identities.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Preslav --- providers/ms365/resources/ms365.lr | 49 +++ providers/ms365/resources/ms365.lr.go | 405 ++++++++++++++++++ .../ms365/resources/ms365.lr.manifest.yaml | 36 ++ providers/ms365/resources/users.go | 138 ++++++ 4 files changed, 628 insertions(+) diff --git a/providers/ms365/resources/ms365.lr b/providers/ms365/resources/ms365.lr index 63b2a468a7..12023f4130 100644 --- a/providers/ms365/resources/ms365.lr +++ b/providers/ms365/resources/ms365.lr @@ -140,6 +140,55 @@ private microsoft.user @defaults("id displayName userPrincipalName") { authMethods() microsoft.user.authenticationMethods // Whether MFA is enabled for the user. mfaEnabled() bool + // The user creation type. + creationType string + // The user's identities. + identities []microsoft.user.identity + // The user's audit-log. + auditlog() microsoft.user.auditlog +} + +// Microsoft User Audit log +private microsoft.user.auditlog { + // The user's identifier. + userId string + // The user's sign-in entries. Only entries from the last 24 hours are fetched and up to 50 at most. + // Note that only interactive sign-in entries are currently returned. + signins() []microsoft.user.signin + // The user's last interactive sign-in + lastInteractiveSignIn() microsoft.user.signin + // The user's last non-interactive sign-in + lastNonInteractiveSignIn() microsoft.user.signin +} + +// Microsoft User Identity +private microsoft.user.identity @defaults("issuerAssignedId") { + // The id as assigned by the issuer. + issuerAssignedId string + // The identity issuer. + issuer string + // The sign-in type for the identity (e.g. 'federated', 'userPrincipalName') + signInType string +} + +// Microsoft User Sign in +private microsoft.user.signin { + // The sign-in entry's identifier. + id string + // The creation time of the sign-in entry. + createdDateTime time + // The id of the user. + userId string + // The display name of the user. + userDisplayName string + // The client app, used to perform the sig-in. + clientAppUsed string + // The app's display name. + appDisplayName string + // The resource display name. + resourceDisplayName string + // Whether the sign-in was interactive. + interactive bool } // Microsoft Entra authentication methods diff --git a/providers/ms365/resources/ms365.lr.go b/providers/ms365/resources/ms365.lr.go index 9755e5006b..df3e18049b 100644 --- a/providers/ms365/resources/ms365.lr.go +++ b/providers/ms365/resources/ms365.lr.go @@ -46,6 +46,18 @@ func init() { Init: initMicrosoftUser, Create: createMicrosoftUser, }, + "microsoft.user.auditlog": { + // to override args, implement: initMicrosoftUserAuditlog(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createMicrosoftUserAuditlog, + }, + "microsoft.user.identity": { + // to override args, implement: initMicrosoftUserIdentity(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createMicrosoftUserIdentity, + }, + "microsoft.user.signin": { + // to override args, implement: initMicrosoftUserSignin(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createMicrosoftUserSignin, + }, "microsoft.user.authenticationMethods": { // to override args, implement: initMicrosoftUserAuthenticationMethods(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createMicrosoftUserAuthenticationMethods, @@ -404,6 +416,60 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "microsoft.user.mfaEnabled": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftUser).GetMfaEnabled()).ToDataRes(types.Bool) }, + "microsoft.user.creationType": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUser).GetCreationType()).ToDataRes(types.String) + }, + "microsoft.user.identities": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUser).GetIdentities()).ToDataRes(types.Array(types.Resource("microsoft.user.identity"))) + }, + "microsoft.user.auditlog": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUser).GetAuditlog()).ToDataRes(types.Resource("microsoft.user.auditlog")) + }, + "microsoft.user.auditlog.userId": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserAuditlog).GetUserId()).ToDataRes(types.String) + }, + "microsoft.user.auditlog.signins": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserAuditlog).GetSignins()).ToDataRes(types.Array(types.Resource("microsoft.user.signin"))) + }, + "microsoft.user.auditlog.lastInteractiveSignIn": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserAuditlog).GetLastInteractiveSignIn()).ToDataRes(types.Resource("microsoft.user.signin")) + }, + "microsoft.user.auditlog.lastNonInteractiveSignIn": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserAuditlog).GetLastNonInteractiveSignIn()).ToDataRes(types.Resource("microsoft.user.signin")) + }, + "microsoft.user.identity.issuerAssignedId": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserIdentity).GetIssuerAssignedId()).ToDataRes(types.String) + }, + "microsoft.user.identity.issuer": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserIdentity).GetIssuer()).ToDataRes(types.String) + }, + "microsoft.user.identity.signInType": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserIdentity).GetSignInType()).ToDataRes(types.String) + }, + "microsoft.user.signin.id": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetId()).ToDataRes(types.String) + }, + "microsoft.user.signin.createdDateTime": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetCreatedDateTime()).ToDataRes(types.Time) + }, + "microsoft.user.signin.userId": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetUserId()).ToDataRes(types.String) + }, + "microsoft.user.signin.userDisplayName": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetUserDisplayName()).ToDataRes(types.String) + }, + "microsoft.user.signin.clientAppUsed": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetClientAppUsed()).ToDataRes(types.String) + }, + "microsoft.user.signin.appDisplayName": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetAppDisplayName()).ToDataRes(types.String) + }, + "microsoft.user.signin.resourceDisplayName": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetResourceDisplayName()).ToDataRes(types.String) + }, + "microsoft.user.signin.interactive": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMicrosoftUserSignin).GetInteractive()).ToDataRes(types.Bool) + }, "microsoft.user.authenticationMethods.count": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMicrosoftUserAuthenticationMethods).GetCount()).ToDataRes(types.Int) }, @@ -1428,6 +1494,90 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlMicrosoftUser).MfaEnabled, ok = plugin.RawToTValue[bool](v.Value, v.Error) return }, + "microsoft.user.creationType": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUser).CreationType, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.identities": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUser).Identities, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "microsoft.user.auditlog": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUser).Auditlog, ok = plugin.RawToTValue[*mqlMicrosoftUserAuditlog](v.Value, v.Error) + return + }, + "microsoft.user.auditlog.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserAuditlog).__id, ok = v.Value.(string) + return + }, + "microsoft.user.auditlog.userId": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserAuditlog).UserId, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.auditlog.signins": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserAuditlog).Signins, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) + return + }, + "microsoft.user.auditlog.lastInteractiveSignIn": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserAuditlog).LastInteractiveSignIn, ok = plugin.RawToTValue[*mqlMicrosoftUserSignin](v.Value, v.Error) + return + }, + "microsoft.user.auditlog.lastNonInteractiveSignIn": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserAuditlog).LastNonInteractiveSignIn, ok = plugin.RawToTValue[*mqlMicrosoftUserSignin](v.Value, v.Error) + return + }, + "microsoft.user.identity.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserIdentity).__id, ok = v.Value.(string) + return + }, + "microsoft.user.identity.issuerAssignedId": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserIdentity).IssuerAssignedId, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.identity.issuer": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserIdentity).Issuer, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.identity.signInType": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserIdentity).SignInType, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).__id, ok = v.Value.(string) + return + }, + "microsoft.user.signin.id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).Id, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.createdDateTime": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).CreatedDateTime, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + return + }, + "microsoft.user.signin.userId": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).UserId, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.userDisplayName": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).UserDisplayName, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.clientAppUsed": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).ClientAppUsed, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.appDisplayName": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).AppDisplayName, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.resourceDisplayName": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).ResourceDisplayName, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "microsoft.user.signin.interactive": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMicrosoftUserSignin).Interactive, ok = plugin.RawToTValue[bool](v.Value, v.Error) + return + }, "microsoft.user.authenticationMethods.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlMicrosoftUserAuthenticationMethods).__id, ok = v.Value.(string) return @@ -3150,6 +3300,9 @@ type mqlMicrosoftUser struct { Contact plugin.TValue[interface{}] AuthMethods plugin.TValue[*mqlMicrosoftUserAuthenticationMethods] MfaEnabled plugin.TValue[bool] + CreationType plugin.TValue[string] + Identities plugin.TValue[[]interface{}] + Auditlog plugin.TValue[*mqlMicrosoftUserAuditlog] } // createMicrosoftUser creates a new instance of this resource @@ -3308,6 +3461,258 @@ func (c *mqlMicrosoftUser) GetMfaEnabled() *plugin.TValue[bool] { }) } +func (c *mqlMicrosoftUser) GetCreationType() *plugin.TValue[string] { + return &c.CreationType +} + +func (c *mqlMicrosoftUser) GetIdentities() *plugin.TValue[[]interface{}] { + return &c.Identities +} + +func (c *mqlMicrosoftUser) GetAuditlog() *plugin.TValue[*mqlMicrosoftUserAuditlog] { + return plugin.GetOrCompute[*mqlMicrosoftUserAuditlog](&c.Auditlog, func() (*mqlMicrosoftUserAuditlog, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.user", c.__id, "auditlog") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.(*mqlMicrosoftUserAuditlog), nil + } + } + + return c.auditlog() + }) +} + +// mqlMicrosoftUserAuditlog for the microsoft.user.auditlog resource +type mqlMicrosoftUserAuditlog struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMicrosoftUserAuditlogInternal it will be used here + UserId plugin.TValue[string] + Signins plugin.TValue[[]interface{}] + LastInteractiveSignIn plugin.TValue[*mqlMicrosoftUserSignin] + LastNonInteractiveSignIn plugin.TValue[*mqlMicrosoftUserSignin] +} + +// createMicrosoftUserAuditlog creates a new instance of this resource +func createMicrosoftUserAuditlog(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftUserAuditlog{ + 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.user.auditlog", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMicrosoftUserAuditlog) MqlName() string { + return "microsoft.user.auditlog" +} + +func (c *mqlMicrosoftUserAuditlog) MqlID() string { + return c.__id +} + +func (c *mqlMicrosoftUserAuditlog) GetUserId() *plugin.TValue[string] { + return &c.UserId +} + +func (c *mqlMicrosoftUserAuditlog) GetSignins() *plugin.TValue[[]interface{}] { + return plugin.GetOrCompute[[]interface{}](&c.Signins, func() ([]interface{}, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.user.auditlog", c.__id, "signins") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.([]interface{}), nil + } + } + + return c.signins() + }) +} + +func (c *mqlMicrosoftUserAuditlog) GetLastInteractiveSignIn() *plugin.TValue[*mqlMicrosoftUserSignin] { + return plugin.GetOrCompute[*mqlMicrosoftUserSignin](&c.LastInteractiveSignIn, func() (*mqlMicrosoftUserSignin, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.user.auditlog", c.__id, "lastInteractiveSignIn") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.(*mqlMicrosoftUserSignin), nil + } + } + + return c.lastInteractiveSignIn() + }) +} + +func (c *mqlMicrosoftUserAuditlog) GetLastNonInteractiveSignIn() *plugin.TValue[*mqlMicrosoftUserSignin] { + return plugin.GetOrCompute[*mqlMicrosoftUserSignin](&c.LastNonInteractiveSignIn, func() (*mqlMicrosoftUserSignin, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("microsoft.user.auditlog", c.__id, "lastNonInteractiveSignIn") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.(*mqlMicrosoftUserSignin), nil + } + } + + return c.lastNonInteractiveSignIn() + }) +} + +// mqlMicrosoftUserIdentity for the microsoft.user.identity resource +type mqlMicrosoftUserIdentity struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMicrosoftUserIdentityInternal it will be used here + IssuerAssignedId plugin.TValue[string] + Issuer plugin.TValue[string] + SignInType plugin.TValue[string] +} + +// createMicrosoftUserIdentity creates a new instance of this resource +func createMicrosoftUserIdentity(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftUserIdentity{ + 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.user.identity", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMicrosoftUserIdentity) MqlName() string { + return "microsoft.user.identity" +} + +func (c *mqlMicrosoftUserIdentity) MqlID() string { + return c.__id +} + +func (c *mqlMicrosoftUserIdentity) GetIssuerAssignedId() *plugin.TValue[string] { + return &c.IssuerAssignedId +} + +func (c *mqlMicrosoftUserIdentity) GetIssuer() *plugin.TValue[string] { + return &c.Issuer +} + +func (c *mqlMicrosoftUserIdentity) GetSignInType() *plugin.TValue[string] { + return &c.SignInType +} + +// mqlMicrosoftUserSignin for the microsoft.user.signin resource +type mqlMicrosoftUserSignin struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMicrosoftUserSigninInternal it will be used here + Id plugin.TValue[string] + CreatedDateTime plugin.TValue[*time.Time] + UserId plugin.TValue[string] + UserDisplayName plugin.TValue[string] + ClientAppUsed plugin.TValue[string] + AppDisplayName plugin.TValue[string] + ResourceDisplayName plugin.TValue[string] + Interactive plugin.TValue[bool] +} + +// createMicrosoftUserSignin creates a new instance of this resource +func createMicrosoftUserSignin(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMicrosoftUserSignin{ + 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.user.signin", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMicrosoftUserSignin) MqlName() string { + return "microsoft.user.signin" +} + +func (c *mqlMicrosoftUserSignin) MqlID() string { + return c.__id +} + +func (c *mqlMicrosoftUserSignin) GetId() *plugin.TValue[string] { + return &c.Id +} + +func (c *mqlMicrosoftUserSignin) GetCreatedDateTime() *plugin.TValue[*time.Time] { + return &c.CreatedDateTime +} + +func (c *mqlMicrosoftUserSignin) GetUserId() *plugin.TValue[string] { + return &c.UserId +} + +func (c *mqlMicrosoftUserSignin) GetUserDisplayName() *plugin.TValue[string] { + return &c.UserDisplayName +} + +func (c *mqlMicrosoftUserSignin) GetClientAppUsed() *plugin.TValue[string] { + return &c.ClientAppUsed +} + +func (c *mqlMicrosoftUserSignin) GetAppDisplayName() *plugin.TValue[string] { + return &c.AppDisplayName +} + +func (c *mqlMicrosoftUserSignin) GetResourceDisplayName() *plugin.TValue[string] { + return &c.ResourceDisplayName +} + +func (c *mqlMicrosoftUserSignin) GetInteractive() *plugin.TValue[bool] { + return &c.Interactive +} + // mqlMicrosoftUserAuthenticationMethods for the microsoft.user.authenticationMethods resource type mqlMicrosoftUserAuthenticationMethods struct { MqlRuntime *plugin.Runtime diff --git a/providers/ms365/resources/ms365.lr.manifest.yaml b/providers/ms365/resources/ms365.lr.manifest.yaml index e88149de7d..819d4209ca 100755 --- a/providers/ms365/resources/ms365.lr.manifest.yaml +++ b/providers/ms365/resources/ms365.lr.manifest.yaml @@ -419,6 +419,8 @@ resources: microsoft.user: fields: accountEnabled: {} + auditlog: + min_mondoo_version: 9.0.0 authMethods: min_mondoo_version: 9.0.0 city: {} @@ -427,11 +429,15 @@ resources: min_mondoo_version: 9.0.0 country: {} createdDateTime: {} + creationType: + min_mondoo_version: 9.0.0 department: {} displayName: {} employeeId: {} givenName: {} id: {} + identities: + min_mondoo_version: 9.0.0 job: min_mondoo_version: 9.0.0 jobTitle: {} @@ -443,6 +449,8 @@ resources: otherMails: {} postalCode: {} settings: {} + signins: + min_mondoo_version: 9.0.0 state: {} streetAddress: {} surname: {} @@ -450,6 +458,14 @@ resources: userType: {} is_private: true min_mondoo_version: 5.15.0 + microsoft.user.auditlog: + fields: + lastInteractiveSignIn: {} + lastNonInteractiveSignIn: {} + signins: {} + userId: {} + is_private: true + min_mondoo_version: 9.0.0 microsoft.user.authenticationMethods: fields: count: {} @@ -463,6 +479,26 @@ resources: windowsHelloMethods: {} is_private: true min_mondoo_version: 9.0.0 + microsoft.user.identity: + fields: + issuer: {} + issuerAssignedId: {} + signInType: {} + is_private: true + min_mondoo_version: 9.0.0 + microsoft.user.signin: + fields: + appDisplayName: {} + clientAppUsed: {} + createdDateTime: {} + id: {} + interactive: {} + requestId: {} + resourceDisplayName: {} + userDisplayName: {} + userId: {} + is_private: true + min_mondoo_version: 9.0.0 ms365.exchangeonline: fields: adminAuditLogConfig: {} diff --git a/providers/ms365/resources/users.go b/providers/ms365/resources/users.go index e7ba145eb8..f16fd1e361 100644 --- a/providers/ms365/resources/users.go +++ b/providers/ms365/resources/users.go @@ -9,6 +9,7 @@ import ( "fmt" "time" + "github.com/microsoftgraph/msgraph-beta-sdk-go/auditlogs" betamodels "github.com/microsoftgraph/msgraph-beta-sdk-go/models" "github.com/microsoftgraph/msgraph-beta-sdk-go/reports" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -23,6 +24,7 @@ import ( var userSelectFields = []string{ "id", "accountEnabled", "city", "companyName", "country", "createdDateTime", "department", "displayName", "employeeId", "givenName", "jobTitle", "mail", "mobilePhone", "otherMails", "officeLocation", "postalCode", "state", "streetAddress", "surname", "userPrincipalName", "userType", + "creationType", "identities", } // users reads all users from Entra ID @@ -169,6 +171,21 @@ func initMicrosoftUser(runtime *plugin.Runtime, args map[string]*llx.RawData) (m } func newMqlMicrosoftUser(runtime *plugin.Runtime, u models.Userable) (*mqlMicrosoftUser, error) { + identities := []interface{}{} + for idx, userId := range u.GetIdentities() { + id := fmt.Sprintf("%s-%d", *u.GetId(), idx) + identity, err := CreateResource(runtime, "microsoft.user.identity", map[string]*llx.RawData{ + "signInType": llx.StringDataPtr(userId.GetSignInType()), + "issuer": llx.StringDataPtr(userId.GetIssuer()), + "issuerAssignedId": llx.StringDataPtr(userId.GetIssuerAssignedId()), + "__id": llx.StringData(id), + }) + if err != nil { + return nil, err + } + identities = append(identities, identity) + } + graphUser, err := CreateResource(runtime, "microsoft.user", map[string]*llx.RawData{ "__id": llx.StringDataPtr(u.GetId()), @@ -193,6 +210,8 @@ func newMqlMicrosoftUser(runtime *plugin.Runtime, u models.Userable) (*mqlMicros "surname": llx.StringDataPtr(u.GetSurname()), "userPrincipalName": llx.StringDataPtr(u.GetUserPrincipalName()), "userType": llx.StringDataPtr(u.GetUserType()), + "creationType": llx.StringDataPtr(u.GetCreationType()), + "identities": llx.ArrayData(identities, types.ResourceLike), }) if err != nil { return nil, err @@ -274,6 +293,125 @@ func (a *mqlMicrosoftUser) populateJobContactData() error { return nil } +func (a *mqlMicrosoftUser) auditlog() (*mqlMicrosoftUserAuditlog, error) { + res, err := CreateResource(a.MqlRuntime, "microsoft.user.auditlog", map[string]*llx.RawData{ + "__id": llx.StringData(a.Id.Data), + "userId": llx.StringData(a.Id.Data), + }) + if err != nil { + return nil, err + } + return res.(*mqlMicrosoftUserAuditlog), nil +} + +func (a *mqlMicrosoftUserAuditlog) signins() ([]interface{}, error) { + ctx := context.Background() + now := time.Now() + dayAgo := now.AddDate(0, 0, -1) + filter := fmt.Sprintf( + "createdDateTime ge %s and createdDateTime lt %s and (userId eq '%s' or contains(tolower(userDisplayName), '%s'))", + dayAgo.Format(time.RFC3339), + now.Format(time.RFC3339), + a.UserId.Data, + a.UserId.Data) + top := int32(50) + res := []interface{}{} + signIns, err := fetchUserSignins(ctx, a.MqlRuntime, filter, top) + if err != nil { + return nil, err + } + for _, s := range signIns { + res = append(res, s) + } + return res, nil +} + +func fetchUserSignins(ctx context.Context, runtime *plugin.Runtime, filter string, top int32) ([]*mqlMicrosoftUserSignin, error) { + conn := runtime.Connection.(*connection.Ms365Connection) + betaClient, err := conn.BetaGraphClient() + if err != nil { + return nil, err + } + orderBy := "createdDateTime desc" + req := &auditlogs.SignInsRequestBuilderGetRequestConfiguration{ + QueryParameters: &auditlogs.SignInsRequestBuilderGetQueryParameters{ + Top: &top, + Filter: &filter, + Orderby: []string{orderBy}, + }, + } + resp, err := betaClient.AuditLogs().SignIns().Get(ctx, req) + if err != nil { + return nil, transformError(err) + } + + res := []*mqlMicrosoftUserSignin{} + for _, s := range resp.GetValue() { + signIn, err := newMqlMicrosoftSignIn(runtime, s) + if err != nil { + return nil, err + } + res = append(res, signIn) + } + return res, nil +} + +func (a *mqlMicrosoftUserAuditlog) lastInteractiveSignIn() (*mqlMicrosoftUserSignin, error) { + signIns := a.GetSignins() + if signIns.Error != nil { + return nil, signIns.Error + } + if len(signIns.Data) == 0 { + return nil, nil + } + + latest := signIns.Data[0].(*mqlMicrosoftUserSignin) + return latest, nil +} + +// Note: the audit log API by default excludes the non-interactive sign-ins. This is a workaround to fetch the last non-interactive sign-in. +// We could also grab those as part of the `sign-ins` query but then the amount of data would be much larger as non-interactive logins are much more frequent. +func (a *mqlMicrosoftUserAuditlog) lastNonInteractiveSignIn() (*mqlMicrosoftUserSignin, error) { + ctx := context.Background() + now := time.Now() + dayAgo := now.AddDate(0, 0, -1) + filter := fmt.Sprintf( + "signInEventTypes/any(t: t ne 'interactiveUser') and createdDateTime ge %s and createdDateTime lt %s and (userId eq '%s' or contains(tolower(userDisplayName), '%s'))", + dayAgo.Format(time.RFC3339), + now.Format(time.RFC3339), + a.UserId.Data, + a.UserId.Data) + top := int32(1) + signIns, err := fetchUserSignins(ctx, a.MqlRuntime, filter, top) + if err != nil { + return nil, err + } + if len(signIns) == 0 { + return nil, nil + } + return signIns[0], nil +} + +func newMqlMicrosoftSignIn(runtime *plugin.Runtime, signIn betamodels.SignInable) (*mqlMicrosoftUserSignin, error) { + mqlSignIn, err := CreateResource(runtime, "microsoft.user.signin", + map[string]*llx.RawData{ + "__id": llx.StringDataPtr(signIn.GetId()), + "id": llx.StringDataPtr(signIn.GetId()), + "createdDateTime": llx.TimeDataPtr(signIn.GetCreatedDateTime()), + "userId": llx.StringDataPtr(signIn.GetUserId()), + "clientAppUsed": llx.StringDataPtr(signIn.GetClientAppUsed()), + "resourceDisplayName": llx.StringDataPtr(signIn.GetResourceDisplayName()), + "userDisplayName": llx.StringDataPtr(signIn.GetUserDisplayName()), + "appDisplayName": llx.StringDataPtr(signIn.GetAppDisplayName()), + "interactive": llx.BoolDataPtr(signIn.GetIsInteractive()), + }) + if err != nil { + return nil, err + } + + return mqlSignIn.(*mqlMicrosoftUserSignin), nil +} + type userJob struct { CompanyName *string `json:"companyName"` JobTitle *string `json:"jobTitle"`