// function to get actual logs from Microsoft // Checks if subscription for specified type is running // if it is not returns error and stops // if it is // makes a log request // gets nextpage header from response // response will contain 0-200 URLS to call for actual records // spins off new thread to pull down the actual records // thread handles writing to the file // makes next page request // waits until all threads are finished public void ThreadedGetLogs() { ISOLogPullLibrary.AppOptions appOptions = new AppOptions(); //Removed do to redirects causing error //Version 2 - allow use //ConsoleSpinner spin = new ConsoleSpinner(); //This if statement has been deprecated for now // subtype in AppOptions has been removed until v2 // If SubscriptionType was not set in command line // try to use value in config file; Allows user to set own default subtype // If not set in config use default Exchange /*if (SubscriptionType == null) * { * if (appOptions.SubscriptionType == "" || appOptions.SubscriptionType == null) * { * SubscriptionType = "Exchange"; * } * else * { * SubscriptionType = appOptions.SubscriptionType; * } * * }*/ try { HttpResponseMessage response; //HttpResponseMessage logResponse; AuthenticationResult authResult = Auth.Result; if (authResult == null) { Console.WriteLine("Error: Error with Certificate authentication, check if cert properly installed and permissions of user"); return; } string content; string request; JArray logs = new JArray(); IEnumerable <string> values = null; // check to see if any subscriptions have been started // if can't find subscriptions print error and exit //*Note - If you are sure subscriptions have been started but are reaching this branch // make sure to check if the certificate was installed properly and that the account // has proper permissions to access this cert. // Service accounts were behaving in a weird way, in which the could not access cert. // If cert was installed on local machine, and not current user, make sure that the // application is run as admin. // call ListSubscription method to handle actual requests var subscriptionList = ListSubscription(); if (subscriptionList == null || subscriptionList.Count == 0) { //using (StreamWriter writer = new StreamWriter("SubscriptionError.log")) //{ Console.WriteLine("Error: No Subscriptions found."); int tmp = StartSubscription(); if (tmp == 0) { Console.WriteLine("Error: Failed to start subscription for {0}", SubscriptionType); Console.WriteLine("Specify <start subtype={0}> to manually start subscription", SubscriptionType); } else { Console.WriteLine("Successfully Started Subscription"); } return; //} } else { // if there is a non empty response, check to see if the specified // subscription type has been started bool found = false; string type = "Audit." + SubscriptionType; foreach (var subscription in subscriptionList) { if (subscription.Value <string>("contentType") == type) { if (subscription.Value <string>("status") == "enabled") { Console.WriteLine(subscription); found = true; } } } if (found == true) { // If found, make default request or use start and end time if specified if (StartTime != null && EndTime != null) { request = String.Format(CultureInfo.InvariantCulture, "https://manage.office.com/api/v1.0/{0}/activity/feed/subscriptions/content?contentType=Audit.{1}&startTime={2}&endTime={3}", appOptions.TenantId, SubscriptionType, StartTime, EndTime); //Console.WriteLine(request); } else { request = String.Format(CultureInfo.InvariantCulture, "https://manage.office.com/api/v1.0/{0}/activity/feed/subscriptions/content?contentType=Audit.{1}", appOptions.TenantId, SubscriptionType); } // use an array of Manual Reset Events to track if threads are done // WaitAll can only handle 64 events // the WaitAll is really only waiting for the last 64 threads to finish // Likelihood of an earlier thread not finishing before the last 64 is very small // Printing out all threads number to verify that they all finish ManualResetEvent[] doneEvents = new ManualResetEvent[64]; int i = 0; int retry = 0; int throttle; do { //spin.Turn(); // Get initial page request or nextpage request // Pass the request and authentication result to HttpGet method to make actual Get request throttle = 0; do { response = HttpGet(request, authResult); //response.Headers.TryGetValues("Status", out values); try { content = response.Content.ReadAsStringAsync().Result; JObject tmpObj = JObject.Parse(content); Console.WriteLine(tmpObj["error"]["code"]); if (tmpObj["error"]["code"].ToString() == "AF429") { Thread.Sleep(5000 + (1000 * throttle)); throttle++; } else { throttle = 20; } }catch { throttle = 20; } }while(throttle < 20); // if the response is not null; // often microsoft returns null response for next page requests // library will retry the request up to 50 times sleeping for 2 seconds // each time if (response != null) { // If next page header is specified in response then store in // variable values response.Headers.TryGetValues("NextPageUri", out values); // Read content from body of response // Content will be a json array in string format of up to 200 urls // Each url must be called to obtain actual records content = response.Content.ReadAsStringAsync().Result; // Parse string to JArray object type try { var records = JArray.Parse(content); // Create new manual reset event which will be passed to thread var done = new ManualResetEvent(false); doneEvents[i % 64] = done; // Create new Log Object type which has the work item method // Pass the urls as records, authentication result of certificate authentication, // the manual reset event, and thread safe Writer, to the contrsuctor class Log l = new Log(records, authResult, done, SafeWriter); // Call ThreadPoolCallback method and add to threadpool work item queue ThreadPool.QueueUserWorkItem(l.ThreadPoolCallback, i); } catch { Console.Error.WriteLine(content); } // if the NextPageUri header was empty, set request to null to end loop // else set request to the NextPageUri if (values == null) { request = null; } else { request = values.First(); } //clear values and response, increment i, and then loop values = null; response = null; i++; } // else branch from if(response !=null) else { // Check if retry attempts below 50 // If it is increment sleep and the increment retry and loop if (retry < 50) { Thread.Sleep(2000); //Console.WriteLine("Error: Retrying ..."); retry++; } // else print error statement, current threads will finish and application // will end early, there is no way to proceed because we cannot get a NextPageUri else { Console.Error.WriteLine("Error: Page Request came back null,"); Console.Error.WriteLine("Error: Not all pages were acquired"); retry = 0; request = null; } } } while (request != null); // Once all the pages have been received and all the threads needed have been created // application needs to wait for threads to finish, since we are using array need to check // if the array was filled or not. // if less than 64 threads were created, application must resize array to correct length if (i < 64) { Array.Resize <ManualResetEvent>(ref doneEvents, (i)); } // Use wait handle all to wait for the last threads to finish int j = i; do { try { Console.WriteLine("Waiting..."); WaitHandle.WaitAll(doneEvents); Console.WriteLine("All threads are complete."); j = 0; } catch { j--; if (i < 64) { Array.Resize <ManualResetEvent>(ref doneEvents, (i)); } } } while (j != 0); SafeWriter.Stitch(i); } // else branch from if(found==true) // reach this branch if subscriptions found but not the subtype specified else { //using (StreamWriter writer = new StreamWriter("SubscriptionError.log")) //{ Console.WriteLine("Error: Subscription {0} was not found", SubscriptionType); int tmp = StartSubscription(); if (tmp == 0) { Console.WriteLine("Error: Failed to start subscription for {0}", SubscriptionType); Console.WriteLine("Specify <start subtype={0}> to manually start subscription", SubscriptionType); } else { Console.WriteLine("Started Subscription"); } return; //} } } } // Catch all branch catch (Exception ex) { Console.Error.WriteLine("{0}:{1}", ex.Message, ex.InnerException); Console.Error.WriteLine("{0}", ex.ToString()); return; } }
// Method which attempts to authenticate with cert private async Task <AuthenticationResult> CertAuthentication() { // Create an instance of AppOptions to get values from config ISOLogPullLibrary.AppOptions appOptions = new AppOptions(); ClientAssertionCertificate cac; AuthenticationResult result = null; AuthenticationContext authContext = null; try { //this happens regardless of authentication method // Find Certificate by thumbprint in Cert Store using getCert method // getCert handles checking different stores for cert and permissions X509Certificate2 cert = getCert(appOptions.CertThumbPrint); // Use clientId and Certificate to create Client Assertion cac = new ClientAssertionCertificate(appOptions.ClientId, cert); // determine if we're using oauth and act accordingly string oAuthUri = appOptions.Authority + appOptions.Oauth2AuthorityId; if (appOptions.UseOauth2) { authContext = new AuthenticationContext(oAuthUri); } else { // Create an Authentication Context // Uses the Authority attribute from AppOptions // This is the AADInstance + Tenant and is not actually listed in config file authContext = new AuthenticationContext(appOptions.Authority); } //Attempt to Authenticate to resource id try { if (appOptions.UseOauth2) { IPlatformParameters ipm = new PlatformParameters(PromptBehavior.Never); //result = await authContext.AcquireTokenAsync(appOptions.ResourceId, appOptions.ClientId, new Uri(appOptions.Oauth2RedirectUi), ipm,); result = await authContext.AcquireTokenAsync(appOptions.ResourceId, cac); } else { // Use the acquire token method and wait until finishes result = await authContext.AcquireTokenAsync(appOptions.ResourceId, cac); } return(result); } catch (Exception ex) { // Return null if can't authenticate Debug.WriteLine(ex.Message); return(null); } } catch (AdalException ex) { if (ex.ErrorCode != "user_interaction_required") { // An unexpected error occurred. Debug.WriteLine(ex.Message); } // An unexpected error occurred, or user canceled the sign in. if (ex.ErrorCode != "access_denied") { Debug.WriteLine(ex.Message); } return(null); } #endregion }