diff --git a/msgraph/applications.go b/msgraph/applications.go index 4edf4e14..70ef8146 100644 --- a/msgraph/applications.go +++ b/msgraph/applications.go @@ -464,3 +464,78 @@ func (c *ApplicationsClient) RemoveOwners(ctx context.Context, applicationId str } return status, nil } + +func (c *ApplicationsClient) ListExtensions(ctx context.Context, id string) (*[]ApplicationExtension, int, error) { + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: fmt.Sprintf("/applications/%s/extensionProperties", id), + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("ApplicationsClient.BaseClient.List(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + + var data struct { + ApplicationExtension []ApplicationExtension `json:"value"` + } + if err := json.Unmarshal(respBody, &data); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + return &data.ApplicationExtension, status, nil +} + +// Create creates a new ApplicationExtention. +func (c *ApplicationsClient) CreateExtension(ctx context.Context, applicationExtention ApplicationExtension, id string) (*ApplicationExtension, int, error) { + var status int + body, err := json.Marshal(applicationExtention) + if err != nil { + return nil, status, fmt.Errorf("json.Marshal(): %v", err) + } + resp, status, _, err := c.BaseClient.Post(ctx, PostHttpRequestInput{ + Body: body, + ValidStatusCodes: []int{http.StatusCreated}, + Uri: Uri{ + Entity: fmt.Sprintf("/applications/%s/extensionProperties", id), + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("ApplicationsClient.BaseClient.Post(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + + var newApplicationExtension ApplicationExtension + if err := json.Unmarshal(respBody, &newApplicationExtension); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + + return &newApplicationExtension, status, nil +} + +// DeleteExtension removes an Application Extension. +func (c *ApplicationsClient) DeleteExtension(ctx context.Context, applicationId, extensionId string) (int, error) { + _, status, _, err := c.BaseClient.Delete(ctx, DeleteHttpRequestInput{ + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusNoContent}, + Uri: Uri{ + Entity: fmt.Sprintf("/applications/%s/extensionProperties/%s", applicationId, extensionId), + HasTenantId: true, + }, + }) + if err != nil { + return status, fmt.Errorf("ApplicationsClient.BaseClient.Delete(): %v", err) + } + return status, nil +} diff --git a/msgraph/applications_test.go b/msgraph/applications_test.go index 63a0f11f..f28e9e40 100644 --- a/msgraph/applications_test.go +++ b/msgraph/applications_test.go @@ -29,6 +29,17 @@ func TestApplicationsClient(t *testing.T) { }) testApplicationsClient_Get(t, c, *app.ID) app.DisplayName = utils.StringPtr(fmt.Sprintf("test-app-updated-%s", c.randomString)) + targetObject := []msgraph.ApplicationExtensionTargetObject{ + msgraph.ApplicationExtensionTargetObjectUser, + } + newExtension := msgraph.ApplicationExtension{ + DataType: msgraph.ApplicationExtensionDataTypeString, + Name: utils.StringPtr("extName"), + TargetObjects: &targetObject, + } + extensionId := testApplicationsClient_CreateExtension(t, c, newExtension, *app.ID) + testApplicationsClient_ListExtension(t, c, *app.ID) + testApplicationsClient_DeleteExtension(t, c, extensionId, *app.ID) testApplicationsClient_Update(t, c, *app) owners := testApplicationsClient_ListOwners(t, c, *app.ID) testApplicationsClient_GetOwner(t, c, *app.ID, (*owners)[0]) @@ -113,6 +124,46 @@ func testApplicationsClient_Get(t *testing.T, c ApplicationsClientTest, id strin return } +func testApplicationsClient_CreateExtension(t *testing.T, c ApplicationsClientTest, applicationExtention msgraph.ApplicationExtension, id string) string { + extension, status, err := c.client.CreateExtension(c.connection.Context, applicationExtention, id) + if err != nil { + t.Fatalf("ApplicationsClient.CreateExtension(): %v", err) + } + if status < 200 || status >= 300 { + t.Fatalf("ApplicationsClient.CreateExtension(): invalid status: %d", status) + } + if extension == nil { + t.Fatal("ApplicationsClient.CreateExtension(): extension was nil") + } + if extension.Id == nil { + t.Fatal("ApplicationsClient.CreateExtension(): extension.Id was nil") + } + return *extension.Id +} + +func testApplicationsClient_ListExtension(t *testing.T, c ApplicationsClientTest, id string) { + extension, status, err := c.client.ListExtensions(c.connection.Context, id) + if err != nil { + t.Fatalf("ApplicationsClient.ListExtensions(): %v", err) + } + if status < 200 || status >= 300 { + t.Fatalf("ApplicationsClient.ListExtensions(): invalid status: %d", status) + } + if extension == nil { + t.Fatal("ApplicationsClient.ListExtensions(): extension was nil") + } +} + +func testApplicationsClient_DeleteExtension(t *testing.T, c ApplicationsClientTest, extensionId, id string) { + status, err := c.client.DeleteExtension(c.connection.Context, id, extensionId) + if err != nil { + t.Fatalf("ApplicationsClient.ListExtensions(): %v", err) + } + if status < 200 || status >= 300 { + t.Fatalf("ApplicationsClient.ListExtensions(): invalid status: %d", status) + } +} + func testApplicationsClient_GetDeleted(t *testing.T, c ApplicationsClientTest, id string) (application *msgraph.Application) { application, status, err := c.client.GetDeleted(c.connection.Context, id) if err != nil { diff --git a/msgraph/directory_audit_reports.go b/msgraph/directory_audit_reports.go new file mode 100644 index 00000000..31c0a5bc --- /dev/null +++ b/msgraph/directory_audit_reports.go @@ -0,0 +1,78 @@ +package msgraph + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" +) + +// DirectoryAuditReportsClient performs operations on directory Audit reports. +type DirectoryAuditReportsClient struct { + BaseClient Client +} + +// NewDirectoryAuditReportsClient returns a new DirectoryAuditReportsClient. +func NewDirectoryAuditReportsClient(tenantId string) *DirectoryAuditReportsClient { + return &DirectoryAuditReportsClient{ + BaseClient: NewClient(VersionBeta, tenantId), + } +} + +// List returns a list of Directory audit report logs, optionally filtered using OData. +func (c *DirectoryAuditReportsClient) List(ctx context.Context, filter string) (*[]DirectoryAudit, int, error) { + params := url.Values{} + if filter != "" { + params.Add("$filter", filter) + } + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: "/auditLogs/directoryAudits", + Params: params, + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("DirectoryAuditReportsClient.BaseClient.Get(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + var data struct { + DirectoryAuditReports []DirectoryAudit `json:"value"` + } + if err := json.Unmarshal(respBody, &data); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + return &data.DirectoryAuditReports, status, nil +} + +// Get retrieves a Directory audit report. +func (c *DirectoryAuditReportsClient) Get(ctx context.Context, id string) (*DirectoryAudit, int, error) { + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: fmt.Sprintf("/auditLogs/directoryAudits/%s", id), + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("DirectoryAuditReportsClient.BaseClient.Get(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + var directoryAuditReport DirectoryAudit + if err := json.Unmarshal(respBody, &directoryAuditReport); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + return &directoryAuditReport, status, nil +} diff --git a/msgraph/directory_audit_reports_test.go b/msgraph/directory_audit_reports_test.go new file mode 100755 index 00000000..45699125 --- /dev/null +++ b/msgraph/directory_audit_reports_test.go @@ -0,0 +1,56 @@ +package msgraph_test + +import ( + "testing" + + "github.com/manicminer/hamilton/auth" + "github.com/manicminer/hamilton/internal/test" + "github.com/manicminer/hamilton/msgraph" +) + +type DirectoryAuditReportsClientTest struct { + connection *test.Connection + client *msgraph.DirectoryAuditReportsClient +} + +func TestDirectoryAuditReportsTest(t *testing.T) { + c := DirectoryAuditReportsClientTest{ + connection: test.NewConnection(auth.MsGraph, auth.TokenVersion2), + } + c.client = msgraph.NewDirectoryAuditReportsClient(c.connection.AuthConfig.TenantID) + c.client.BaseClient.Authorizer = c.connection.Authorizer + + auditLogs := testDirectoryAuditReports_List(t, c) + testDirectoryAuditReports_Get(t, c, *(*auditLogs)[0].Id) +} + +func testDirectoryAuditReports_List(t *testing.T, c DirectoryAuditReportsClientTest) (dirLogs *[]msgraph.DirectoryAudit) { + dirLogs, status, err := c.client.List(c.connection.Context, "") + + if status < 200 || status >= 300 { + t.Fatalf("DirectoryAuditReportsClient.List(): invalid status: %d", status) + } + + if err != nil { + t.Fatalf("DirectoryAuditReportsClient.List(): %v", err) + } + + if dirLogs == nil { + t.Fatal("DirectoryAuditReportsClient.List():logs was nil") + } + return dirLogs +} + +func testDirectoryAuditReports_Get(t *testing.T, c DirectoryAuditReportsClientTest, id string) (dirLog *msgraph.DirectoryAudit) { + dirLog, status, err := c.client.Get(c.connection.Context, id) + if err != nil { + t.Fatalf("DirectoryAuditReportsClient.Get(): %v", err) + } + if status < 200 || status >= 300 { + t.Fatalf("DirectoryAuditReportsClient.Get(): invalid status: %d", status) + } + if dirLog == nil { + t.Fatal("DirectoryAuditReportsClient.Get(): domain was nil") + } + return dirLog +} diff --git a/msgraph/models.go b/msgraph/models.go index ce20ef1d..48ba91b0 100644 --- a/msgraph/models.go +++ b/msgraph/models.go @@ -27,6 +27,13 @@ type ApiPreAuthorizedApplication struct { PermissionIds *[]string `json:"permissionIds,omitempty"` } +type AppIdentity struct { + AppId *string `json:"appId,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + ServicePrincipalId *string `json:"servicePrincipalId,omitempty"` + ServicePrincipalName *string `json:"servicePrincipalName,omitempty"` +} + // Application describes an Application object. type Application struct { ID *string `json:"id,omitempty"` @@ -273,6 +280,15 @@ type ApplicationEnforcedRestrictionsSessionControl struct { IsEnabled *bool `json:"isEnabled,omitempty"` } +type ApplicationExtension struct { + Id *string `json:"id,omitempty"` + AppDisplayName *string `json:"appDisplayName,omitempty"` + DataType ApplicationExtensionDataType `json:"dataType,omitempty"` + IsSyncedFromOnPremises *bool `json:"isSyncedFromOnPremises,omitempty"` + Name *string `json:"name,omitempty"` + TargetObjects *[]ApplicationExtensionTargetObject `json:"targetObjects,omitempty"` +} + type ApplicationWeb struct { HomePageUrl *StringNullWhenEmpty `json:"homePageUrl,omitempty"` ImplicitGrantSettings *ImplicitGrantSettings `json:"implicitGrantSettings,omitempty"` @@ -280,6 +296,14 @@ type ApplicationWeb struct { RedirectUris *[]string `json:"redirectUris,omitempty"` } +type AppliedConditionalAccessPolicy struct { + DisplayName *string `json:"displayName,omitempty"` + EnforcedGrantControls *[]string `json:"enforcedGrantControls,omitempty"` + EnforcedSessionControls *[]string `json:"enforcedSessionControls,omitempty"` + Id *string `json:"id,omitempty"` + Result *string `json:"appliedConditionalAccessPolicyResult,omitempty"` +} + type AppRole struct { ID *string `json:"id,omitempty"` AllowedMemberTypes *[]AppRoleAllowedMemberType `json:"allowedMemberTypes,omitempty"` @@ -302,6 +326,11 @@ type AppRoleAssignment struct { ResourceId *string `json:"resourceId,omitempty"` } +type AuditActivityInitiator struct { + App *AppIdentity `json:"app,omitempty"` + User *UserIdentity `json:"user,omitempty"` +} + type BaseNamedLocation struct { ODataType *string `json:"@odata.type,omitempty"` ID *string `json:"id,omitempty"` @@ -315,16 +344,10 @@ type CloudAppSecurityControl struct { CloudAppSecurityType *string `json:"cloudAppSecurityType,omitempty"` } -// ConditionalAccessPolicy describes an Conditional Access Policy object. -type ConditionalAccessPolicy struct { - Conditions *ConditionalAccessConditionSet `json:"conditions,omitempty"` - CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` - DisplayName *string `json:"displayName,omitempty"` - GrantControls *ConditionalAccessGrantControls `json:"grantControls,omitempty"` - ID *string `json:"id,omitempty"` - ModifiedDateTime *time.Time `json:"modifiedDateTime,omitempty"` - SessionControls *ConditionalAccessSessionControls `json:"sessionControls,omitempty"` - State *string `json:"state,omitempty"` +type ConditionalAccessApplications struct { + IncludeApplications *[]string `json:"includeApplications,omitempty"` + ExcludeApplications *[]string `json:"excludeApplications,omitempty"` + IncludeUserActions *[]string `json:"includeUserActions,omitempty"` } type ConditionalAccessConditionSet struct { @@ -337,19 +360,11 @@ type ConditionalAccessConditionSet struct { UserRiskLevels *[]string `json:"userRiskLevels,omitempty"` } -type ConditionalAccessApplications struct { - IncludeApplications *[]string `json:"includeApplications,omitempty"` - ExcludeApplications *[]string `json:"excludeApplications,omitempty"` - IncludeUserActions *[]string `json:"includeUserActions,omitempty"` -} - -type ConditionalAccessUsers struct { - IncludeUsers *[]string `json:"includeUsers,omitempty"` - ExcludeUsers *[]string `json:"excludeUsers,omitempty"` - IncludeGroups *[]string `json:"includeGroups,omitempty"` - ExcludeGroups *[]string `json:"excludeGroups,omitempty"` - IncludeRoles *[]string `json:"includeRoles,omitempty"` - ExcludeRoles *[]string `json:"excludeRoles,omitempty"` +type ConditionalAccessGrantControls struct { + Operator *string `json:"operator,omitempty"` + BuiltInControls *[]string `json:"builtInControls,omitempty"` + CustomAuthenticationFactors *[]string `json:"customAuthenticationFactors,omitempty"` + TermsOfUse *[]string `json:"termsOfUse,omitempty"` } type ConditionalAccessLocations struct { @@ -362,11 +377,16 @@ type ConditionalAccessPlatforms struct { ExcludePlatforms *[]string `json:"excludePlatforms,omitempty"` } -type ConditionalAccessGrantControls struct { - Operator *string `json:"operator,omitempty"` - BuiltInControls *[]string `json:"builtInControls,omitempty"` - CustomAuthenticationFactors *[]string `json:"customAuthenticationFactors,omitempty"` - TermsOfUse *[]string `json:"termsOfUse,omitempty"` +// ConditionalAccessPolicy describes an Conditional Access Policy object. +type ConditionalAccessPolicy struct { + Conditions *ConditionalAccessConditionSet `json:"conditions,omitempty"` + CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + GrantControls *ConditionalAccessGrantControls `json:"grantControls,omitempty"` + ID *string `json:"id,omitempty"` + ModifiedDateTime *time.Time `json:"modifiedDateTime,omitempty"` + SessionControls *ConditionalAccessSessionControls `json:"sessionControls,omitempty"` + State *string `json:"state,omitempty"` } type ConditionalAccessSessionControls struct { @@ -376,6 +396,15 @@ type ConditionalAccessSessionControls struct { SignInFrequency *SignInFrequencySessionControl `json:"signInFrequency,omitempty"` } +type ConditionalAccessUsers struct { + IncludeUsers *[]string `json:"includeUsers,omitempty"` + ExcludeUsers *[]string `json:"excludeUsers,omitempty"` + IncludeGroups *[]string `json:"includeGroups,omitempty"` + ExcludeGroups *[]string `json:"excludeGroups,omitempty"` + IncludeRoles *[]string `json:"includeRoles,omitempty"` + ExcludeRoles *[]string `json:"excludeRoles,omitempty"` +} + // CountryNamedLocation describes an Country Named Location object. type CountryNamedLocation struct { *BaseNamedLocation @@ -383,12 +412,28 @@ type CountryNamedLocation struct { IncludeUnknownCountriesAndRegions *bool `json:"includeUnknownCountriesAndRegions,omitempty"` } -// DirectoryRoleTemplate describes a Directory Role Template. -type DirectoryRoleTemplate struct { - ID *string `json:"id,omitempty"` - DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` - Description *string `json:"description,omitempty"` - DisplayName *string `json:"displayName,omitempty"` +type DeviceDetail struct { + Browser *string `json:"browser,omitempty"` + DeviceId *string `json:"deviceId,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + IsCompliant *bool `json:"isCompliant,omitempty"` + IsManaged *bool `json:"isManaged,omitempty"` + OperatingSystem *string `json:"operatingSystem,omitempty"` + TrustType *string `json:"trustType,omitempty"` +} + +type DirectoryAudit struct { + ActivityDateTime *time.Time `json:"activityDateTime,omitempty"` + ActivityDisplayName *string `json:"activityDisplayName,omitempty"` + AdditionalDetails *[]KeyValue `json:"additionalDetails,omitempty"` + Category *string `json:"category,omitempty"` + CorrelationId *string `json:"correlationId,omitempty"` + Id *string `json:"id,omitempty"` + InitiatedBy *AuditActivityInitiator `json:"initiatedBy,omitempty"` + LoggedByService *string `json:"loggedByService,omitempty"` + Result *string `json:"result,omitempty"` + ResultReason *string `json:"resultReason,omitempty"` + TargetResources *[]TargetResource `json:"targetResources,omitempty"` } type DirectoryRole struct { @@ -411,6 +456,14 @@ func (d *DirectoryRole) AppendMember(endpoint environments.ApiEndpoint, apiVersi d.Members = &members } +// DirectoryRoleTemplate describes a Directory Role Template. +type DirectoryRoleTemplate struct { + ID *string `json:"id,omitempty"` + DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` + Description *string `json:"description,omitempty"` + DisplayName *string `json:"displayName,omitempty"` +} + // Domain describes a Domain object. type Domain struct { ID *string `json:"id,omitempty"` @@ -438,6 +491,12 @@ type EmailAddress struct { Name *string `json:"name,omitempty"` } +type GeoCoordinates struct { + Altitude *float64 `json:"altitude,omitempty"` + Latitude *float64 `json:"latitude,omitempty"` + Longitude *float64 `json:"longitude,omitempty"` +} + // Group describes a Group object. type Group struct { ID *string `json:"id,omitempty"` @@ -599,6 +658,18 @@ type KeyCredential struct { Key *string `json:"key,omitempty"` } +type KeyValue struct { + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` +} + +type Location struct { + City *string `json:"city,omitempty"` + CountryOrRegion *string `json:"countryOrRegion,omitempty"` + GeoCoordinates *GeoCoordinates `json:"geoCoordinates,omitempty"` + State *string `json:"state,omitempty"` +} + type MailMessage struct { Message *Message `json:"message,omitempty"` } @@ -620,6 +691,12 @@ type Message struct { BccRecipients *[]Recipient `json:"bccRecipients,omitempty"` } +type ModifiedProperty struct { + DisplayName *string `json:"displayName,omitempty"` + NewValue *string `json:"newValue,omitempty"` + OldValue *string `json:"oldValue,omitempty"` +} + type NamedLocation interface{} type OnPremisesPublishing struct { @@ -780,6 +857,32 @@ type SignInFrequencySessionControl struct { Value *int32 `json:"value,omitempty"` } +type SignInReport struct { + Id *string `json:"id,omitempty"` + CreatedDateTime *time.Time `json:"createdDateTime,omitempty"` + UserDisplayName *string `json:"userDisplayName,omitempty"` + UserPrincipalName *string `json:"userPrincipalName,omitempty"` + UserId *string `json:"userId,omitempty"` + AppId *string `json:"appId,omitempty"` + AppDisplayName *string `json:"appDisplayName,omitempty"` + IPAddress *string `json:"ipAddress,omitempty"` + ClientAppUsed *string `json:"clientAppUsed,omitempty"` + CorrelationId *string `json:"correlationId,omitempty"` + ConditionalAccessStatus *string `json:"conditionalAccessStatus,omitempty"` + IsInteractive *bool `json:"isInteractive,omitempty"` + RiskDetail *string `json:"riskDetail,omitempty"` + RiskLevelAggregated *string `json:"riskLevelAggregated,omitempty"` + RiskLevelDuringSignIn *string `json:"riskLevelDuringSignIn,omitempty"` + RiskState *string `json:"riskState,omitempty"` + RiskEventTypes *[]string `json:"riskEventTypes,omitempty"` + ResourceDisplayName *string `json:"resourceDisplayName,omitempty"` + ResourceId *string `json:"resourceId,omitempty"` + Status *Status `json:"status,omitempty"` + DeviceDetail *DeviceDetail `json:"deviceDetail,omitempty"` + Location *Location `json:"location,omitempty"` + AppliedConditionalAccessPolicies *[]AppliedConditionalAccessPolicy `json:"appliedConditionalAccessPolicies,omitempty"` +} + type SingleSignOnField struct { CustomizedLabel *string `json:"customizedLabel,omitempty"` DefaultLabel *string `json:"defaultLabel,omitempty"` @@ -787,6 +890,21 @@ type SingleSignOnField struct { Type *string `json:"type,omitempty"` } +type Status struct { + ErrorCode *int32 `json:"errorCode,omitempty"` + FailureReason *string `json:"failureReason,omitempty"` + AdditionalDetails *string `json:"additionalDetails,omitempty"` +} + +type TargetResource struct { + Id *string `json:"id,omitempty"` + DisplayName *string `json:"displayName,omitempty"` + Type *string `json:"type,omitempty"` + UserPrincipalName *string `json:"userPrincipalName,omitempty"` + GroupType *string `json:"groupType,omitempty"` + ModifiedProperties *[]ModifiedProperty `json:"modifiedProperties,omitempty"` +} + // User describes a User object. type User struct { ID *string `json:"id,omitempty"` @@ -849,6 +967,13 @@ type User struct { PasswordProfile *UserPasswordProfile `json:"passwordProfile,omitempty"` } +type UserIdentity struct { + DisplayName *string `json:"displayName,omitempty"` + Id *string `json:"id,omitempty"` + IPAddress *string `json:"ipAddress,omitempty"` + UserPrincipalName *string `json:"userPrincipalName,omitempty"` +} + type UserPasswordProfile struct { ForceChangePasswordNextSignIn *bool `json:"forceChangePasswordNextSignIn,omitempty"` ForceChangePasswordNextSignInWithMfa *bool `json:"forceChangePasswordNextSignInWithMfa,omitempty"` diff --git a/msgraph/sign_in_reports.go b/msgraph/sign_in_reports.go new file mode 100644 index 00000000..aaf365d3 --- /dev/null +++ b/msgraph/sign_in_reports.go @@ -0,0 +1,78 @@ +package msgraph + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" +) + +// SignInReports Client performs operations on Sign in reports. +type SignInReportsClient struct { + BaseClient Client +} + +// NewSignInLogsClient returns a new SignInReportsClient. +func NewSignInLogsClient(tenantId string) *SignInReportsClient { + return &SignInReportsClient{ + BaseClient: NewClient(VersionBeta, tenantId), + } +} + +// List returns a list of Sign-in Reports, optionally filtered using OData. +func (c *SignInReportsClient) List(ctx context.Context, filter string) (*[]SignInReport, int, error) { + params := url.Values{} + if filter != "" { + params.Add("$filter", filter) + } + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: "/auditLogs/signIns", + Params: params, + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("SignInLogsClient.BaseClient.Get(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + var data struct { + SignInLogs []SignInReport `json:"value"` + } + if err := json.Unmarshal(respBody, &data); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + return &data.SignInLogs, status, nil +} + +// Get retrieves a Sign-in Report. +func (c *SignInReportsClient) Get(ctx context.Context, id string) (*SignInReport, int, error) { + resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{ + ConsistencyFailureFunc: RetryOn404ConsistencyFailureFunc, + ValidStatusCodes: []int{http.StatusOK}, + Uri: Uri{ + Entity: fmt.Sprintf("/auditLogs/signIns/%s", id), + HasTenantId: true, + }, + }) + if err != nil { + return nil, status, fmt.Errorf("SignInLogsClient.BaseClient.Get(): %v", err) + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err) + } + var signInReport SignInReport + if err := json.Unmarshal(respBody, &signInReport); err != nil { + return nil, status, fmt.Errorf("json.Unmarshal(): %v", err) + } + return &signInReport, status, nil +} diff --git a/msgraph/sign_in_reports_test.go b/msgraph/sign_in_reports_test.go new file mode 100644 index 00000000..d325c3dc --- /dev/null +++ b/msgraph/sign_in_reports_test.go @@ -0,0 +1,56 @@ +package msgraph_test + +import ( + "testing" + + "github.com/manicminer/hamilton/auth" + "github.com/manicminer/hamilton/internal/test" + "github.com/manicminer/hamilton/msgraph" +) + +type SignInReportsClientTest struct { + connection *test.Connection + client *msgraph.SignInReportsClient +} + +func TestSignInReportsTest(t *testing.T) { + c := SignInReportsClientTest{ + connection: test.NewConnection(auth.MsGraph, auth.TokenVersion2), + } + c.client = msgraph.NewSignInLogsClient(c.connection.AuthConfig.TenantID) + c.client.BaseClient.Authorizer = c.connection.Authorizer + + signInLogs := testSignInReports_List(t, c) + testSignInReports_Get(t, c, *(*signInLogs)[0].Id) +} + +func testSignInReports_List(t *testing.T, c SignInReportsClientTest) (signInLogs *[]msgraph.SignInReport) { + signInLogs, status, err := c.client.List(c.connection.Context, "") + + if status < 200 || status >= 300 { + t.Fatalf("SignInReportsClient.List(): invalid status: %d", status) + } + + if err != nil { + t.Fatalf("SignInReportsClient.List(): %v", err) + } + + if signInLogs == nil { + t.Fatal("SignInReportsClient.List():logs was nil") + } + return +} + +func testSignInReports_Get(t *testing.T, c SignInReportsClientTest, id string) (signInLog *msgraph.SignInReport) { + signInLog, status, err := c.client.Get(c.connection.Context, id) + if err != nil { + t.Fatalf("SignInReportsClient.Get(): %v", err) + } + if status < 200 || status >= 300 { + t.Fatalf("SignInReportsClient.Get(): invalid status: %d", status) + } + if signInLog == nil { + t.Fatal("SignInReportsClient.Get(): domain was nil") + } + return +} diff --git a/msgraph/valuetypes.go b/msgraph/valuetypes.go index 41d40787..ed108b32 100644 --- a/msgraph/valuetypes.go +++ b/msgraph/valuetypes.go @@ -14,6 +14,27 @@ func (s StringNullWhenEmpty) MarshalJSON() ([]byte, error) { return json.Marshal(string(s)) } +type ApplicationExtensionDataType string + +const ( + ApplicationExtensionDataTypeBinary ApplicationExtensionDataType = "Binary" + ApplicationExtensionDataTypeBoolean ApplicationExtensionDataType = "Boolean" + ApplicationExtensionDataTypeDateTime ApplicationExtensionDataType = "DateTime" + ApplicationExtensionDataTypeInteger ApplicationExtensionDataType = "Integer" + ApplicationExtensionDataTypeLargeInteger ApplicationExtensionDataType = "LargeInteger" + ApplicationExtensionDataTypeString ApplicationExtensionDataType = "String" +) + +type ApplicationExtensionTargetObject string + +const ( + ApplicationExtensionTargetObjectApplication ApplicationExtensionTargetObject = "Application" + ApplicationExtensionTargetObjectDevice ApplicationExtensionTargetObject = "Device" + ApplicationExtensionTargetObjectGroup ApplicationExtensionTargetObject = "Group" + ApplicationExtensionTargetObjectOrganization ApplicationExtensionTargetObject = "Organization" + ApplicationExtensionTargetObjectUser ApplicationExtensionTargetObject = "User" +) + type AppRoleAllowedMemberType string const (