public void GraphApiHelperTestSetup()
        {
            var logger      = new Mock <ILogger <GraphApiHelper> >().Object;
            var memoryCache = new Mock <IMemoryCache>().Object;

            graphApiHelper = new GraphApiHelper(logger, memoryCache, ConfigurationData.botOptions);
        }
Exemple #2
0
        public async Task <ActionResult> SendMessageSubmit(UserInfo userInfo)
        {
            // After Index method renders the View, user clicks Send Mail, which comes in here.
            EnsureUser(ref userInfo);
            SendMessageResponse sendMessageResult = new SendMessageResponse();
            // Send email using the Microsoft Graph API.
            var token = Data.GetUserSessionTokenAny(Settings.GetUserAuthStateId(ControllerContext.HttpContext));

            if (token.Provider == Settings.AzureADAuthority || token.Provider == Settings.AzureAD2Authority)
            {
                sendMessageResult = await GraphApiHelper.SendMessageAsync(
                    token.AccessToken,
                    GenerateEmail(userInfo));
            }
            else if (token.Provider == Settings.GoogleAuthority)
            {
                sendMessageResult = await GoogleApiHelper.SendMessageAsync(token.AccessToken, GenerateEmail(userInfo), token.Username);
            }
            // Reuse the Index view for messages (sent, not sent, fail) .
            // Redirect to tell the browser to call the app back via the Index method.
            return(RedirectToAction(nameof(Index), new RouteValueDictionary(new Dictionary <string, object> {
                { "Status", sendMessageResult.Status },
                { "StatusMessage", sendMessageResult.StatusMessage },
                { "Address", userInfo.Address },
            })));
        }
Exemple #3
0
        private async Task LoadAndShowProfileInfoAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var tokenState  = (TokenState)stepContext.Values["accessToken"];
            var currentUser = await GraphApiHelper.GetCurrentUserAsync(tokenState, cancellationToken);

            await stepContext.Context.SendActivityAsync($"You are {currentUser.DisplayName}.", cancellationToken : cancellationToken);
        }
Exemple #4
0
        public static async Task CheckinFromDrive(this Record record, string driveId, string token, bool saveRecord = false)
        {
            string downloadUrl = GraphApiHelper.GetOneDriveItemContentIdUrl(driveId);

            var fileResult = await ODataHelper.GetItem <OneDriveItem>(GraphApiHelper.GetOneDriveItemIdUrl(driveId), token, null);

            string filePath = Path.Combine(TrimApplication.WebServerWorkPath, fileResult.Name);


            await ODataHelper.GetItem <string>(downloadUrl, token, filePath);

            var inputDocument = new InputDocument(filePath);


            inputDocument.CheckinAs = record.SuggestedFileName;
            record.SetDocument(inputDocument, true, false, "checkin from Word Online");

            string pdfPath = Path.Combine(TrimApplication.WebServerWorkPath, Path.ChangeExtension(fileResult.Name, "pdf"));
            string pdfUrl  = GraphApiHelper.GetOneDriveItemContentIdUrl(driveId, "pdf");
            await ODataHelper.GetItem <string>(pdfUrl, token, pdfPath);


            var rendition = record.ChildRenditions.NewRendition(pdfPath, RenditionType.Longevity, "Preview");


            if (saveRecord)
            {
                record.Save();

                File.Delete(filePath);
                File.Delete(pdfPath);
            }
            return;
        }
Exemple #5
0
        /// <summary>
        /// Gets all the charts in the specified Excel workbook and presents them in a view.
        /// </summary>
        /// <param name="id">The internal ID of the workbook.</param>
        /// <returns>The view with the list of charts.</returns>
        public async Task <ActionResult> Index(string id)
        {
            // Get access token from the local database
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            var sheetsUrl = GraphApiHelper.GetSheetsWithChartsUrl(id, "&$select=name,id");
            var sheets    = await ODataHelper.GetItems <ExcelSheet>(sheetsUrl, token.AccessToken);

            // Merge the charts from each worksheet into a single list
            List <Chart> allChartsInWorkbook = new List <Chart>();

            foreach (var sheet in sheets)
            {
                var chartsFromSheet = sheet.Charts;

                // The GetChartImage method requires a clean charts URL, that is, no $select option.
                string cleanFullChartsUrl = GraphApiHelper.GetChartsUrl(id, sheet.Id, null);

                foreach (var chart in chartsFromSheet)
                {
                    // string singleChartImageUrl = GraphApiHelper.GetSingleChartImageUrl(cleanFullChartsUrl, chart.Id);
                    chart.ImageAsBase64String = await GraphApiHelper.GetChartImage(cleanFullChartsUrl, chart.Id, token.AccessToken);
                }

                allChartsInWorkbook = allChartsInWorkbook.Concat(chartsFromSheet).ToList();
            }
            return(View(allChartsInWorkbook));
        }
        public void GraphApiHelper_ThrowsArgumentNullException()
        {
            var logger      = new Mock <ILogger <GraphApiHelper> >().Object;
            var memoryCache = new Mock <IMemoryCache>().Object;

            constructor_ArgumentNullException = new GraphApiHelper(logger, memoryCache, null);
        }
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            // OWIN middleware validated the audience, but the scope must also be validated. It must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (!(addinScopes.Contains("access_as_user")))
            {
                return(HttpErrorHelper.SendErrorToClient(HttpStatusCode.Unauthorized, null, "Missing access_as_user."));
            }

            // Assemble all the information that is needed to get a token for Microsoft Graph using the "on behalf of" flow.
            // Beginning with MSAL.NET 3.x.x, the bootstrapContext is just the bootstrap token itself.
            string        bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext.ToString();
            UserAssertion userAssertion    = new UserAssertion(bootstrapContext);

            var cca = ConfidentialClientApplicationBuilder.Create(ConfigurationManager.AppSettings["ida:ClientID"])
                      .WithRedirectUri("https://localhost:44355")
                      .WithClientSecret(ConfigurationManager.AppSettings["ida:Password"])
                      .WithAuthority(ConfigurationManager.AppSettings["ida:Authority"])
                      .Build();

            // MSAL.NET adds the profile, offline_access, and openid scopes itself. It will throw an error if you add
            // them redundantly here.
            string[] graphScopes = { "https://graph.microsoft.com/Files.Read.All" };

            // Get the access token for Microsoft Graph.
            AcquireTokenOnBehalfOfParameterBuilder parameterBuilder = null;
            AuthenticationResult authResult = null;

            try
            {
                parameterBuilder = cca.AcquireTokenOnBehalfOf(graphScopes, userAssertion);
                authResult       = await parameterBuilder.ExecuteAsync();
            }
            catch (MsalServiceException e)
            {
                // Handle request for multi-factor authentication.
                if (e.Message.StartsWith("AADSTS50076"))
                {
                    string responseMessage = String.Format("{{\"AADError\":\"AADSTS50076\",\"Claims\":{0}}}", e.Claims);
                    return(HttpErrorHelper.SendErrorToClient(HttpStatusCode.Forbidden, null, responseMessage));
                    // The client should recall the getAccessToken function and pass the claims string as the
                    // authChallenge value in the function's Options parameter.
                }

                // Handle lack of consent (AADSTS65001) and invalid scope (permission).
                if ((e.Message.StartsWith("AADSTS65001")) || (e.Message.StartsWith("AADSTS70011: The provided value for the input parameter 'scope' is not valid.")))
                {
                    return(HttpErrorHelper.SendErrorToClient(HttpStatusCode.Forbidden, e, null));
                }

                // Handle all other MsalServiceExceptions.
                else
                {
                    throw e;
                }
            }

            return(await GraphApiHelper.GetOneDriveFileNames(authResult.AccessToken));
        }
Exemple #8
0
        // GET api/values
        public async Task <IEnumerable <string> > Get()
        {
            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;
                UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token);

                // Get the access token for MS Graph.
                ClientCredential clientCred       = new ClientCredential(ConfigurationManager.AppSettings["ida:Password"]);
                ConfidentialClientApplication cca =
                    new ConfidentialClientApplication(ConfigurationManager.AppSettings["ida:ClientID"],
                                                      "https://localhost:44355", clientCred, null, null);
                string[]             graphScopes = { "Files.Read.All" };
                AuthenticationResult result      = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will first look in the MSAL in memory cache for a
                    // matching access token. Only if there isn't one, does it initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await cca.AcquireTokenOnBehalfOfAsync(graphScopes, userAssertion, "https://login.microsoftonline.com/common/oauth2/v2.0");
                }
                catch (MsalUiRequiredException e)
                {
                    // If multi-factor authentication is required by the MS Graph resource an
                    // the user has not yet provided it, AAD will throw an exception containing a
                    // Claims property.
                    if (String.IsNullOrEmpty(e.Claims))
                    {
                        throw e;
                    }
                    else
                    {
                        // The Claims property value must be passed to the client which will pass it
                        // to the Office host, which will then include it in a request for a new token.
                        // AAD will prompt the user for all required forms of authentication.
                        throw new HttpException(e.Claims);
                    }
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");
                var getFilesResult       = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in getFilesResult)
                {
                    itemNames.Add(item.Name);
                }
                return(itemNames);
            }
            return(new string[] { "Error", "Microsoft Office does not have permission to get Microsoft Graph data on behalf of the current user." });
        }
        /// <summary>
        /// Gets emails with conversation id
        /// </summary>
        /// <returns>Emails with the specific conversation id.</returns>
        public async Task <JsonResult> ConversationMessages(string convoId)
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            var messages = await GraphApiHelper.getConversationIdMessages(token.AccessToken, convoId);

            return(Json(messages, JsonRequestBehavior.AllowGet));
        }
        public async Task <dynamic> deleteEmailAttachments(string[] attachmentIds, string emailId, string[] attachmentUrls)
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            var attachments = await GraphApiHelper.deleteEmailAttachments(token.AccessToken, attachmentIds, emailId, attachmentUrls);

            return(Json(attachments, JsonRequestBehavior.AllowGet));
        }
Exemple #11
0
        private async Task LoadAndShowOneDriveItemsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var tokenState    = (TokenState)stepContext.Values["accessToken"];
            var oneDriveItems = await GraphApiHelper.GetOneDriveFilesListAsync(tokenState, cancellationToken);

            await Cards.ShowActivityWithAttachmentsAsync(
                stepContext.Context,
                Cards.BuildOneDriveAttachmentList(oneDriveItems),
                cancellationToken : cancellationToken);
        }
        /// <summary>
        /// Retrieves all messages in a specific user mailFolder
        /// </summary>
        public async Task <Microsoft.Graph.IMailFolderMessagesCollectionPage> getMailFolderMessages(string folderId, string requestUri, string callbackToken)
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            // TODO: Comes in chunks of 10, need to iterate through all...
            var messages = await GraphApiHelper.getMailFolderMessages(folderId, token.AccessToken, requestUri, callbackToken);


            return(messages);
        }
        /// <summary>
        /// Recursively searches OneDrive for Business.
        /// </summary>
        /// <returns>A view listing the workbooks in OneDrive for Business.</returns>
        public async Task <ActionResult> OneDriveFiles()
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            // Get all the Excel files in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
            var fullWorkbooksSearchUrl = GraphApiHelper.GetWorkbookSearchUrl("?$select=name,id");
            var getFilesResult         = await ODataHelper.GetItems <ExcelWorkbook>(fullWorkbooksSearchUrl, token.AccessToken);

            return(View(getFilesResult));
        }
        /// <summary>
        /// Adds hyperlink to OneDrive in email's body if attachments present.
        /// </summary>
        /// <returns> Email's new body with hyperlink </returns>
        public async Task <string> addAttachmentsToBody(string attachmentsLocation, string emailId, string accessToken)
        {
            // Get access token
            // Was commented out
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            string body = await GraphApiHelper.getMessageBody(token.AccessToken, emailId);

            // For embedded images, remove <img > before adding links
            string img;
            string hyperlink;
            bool   oneLinkAvailable = true;

            do
            {
                img = Format.getBetween(body, "<img ", ">");

                // Replace with hyperlink to attachment
                string delete = "<img " + img + ">";
                hyperlink = "<a href=\"" + attachmentsLocation + "\"> View Attachments </a>";

                // Replace first <img > with hyperlink
                if (oneLinkAvailable && img != "")
                {
                    body             = body.Replace(delete, hyperlink);
                    oneLinkAvailable = false;
                }
                else
                {
                    body = body.Replace(delete, "");
                }
            } while (img != "");


            string pageBreak = "<div style=\"font - family:Calibri,Arial,Helvetica,sans - serif; font - size:12pt; color: rgb(0, 0, 0)\"><br></div>";

            // If no embedded attachments, add a link at the beginning of the email
            if (oneLinkAvailable)
            {
                string oldBody = Format.getBetween(body, "<body dir=\"ltr\">", "</body>");
                string newBody = hyperlink + pageBreak + oldBody;

                oldBody = "<body dir=\"ltr\">" + oldBody + "</body>";
                newBody = "<body dir=\"ltr\">" + newBody + "</body>";

                body = body.Replace(oldBody, newBody);
            }
            return(body);
        }
Exemple #15
0
        /// <summary>
        /// Recursively searches OneDrive for Business.
        /// </summary>
        /// <returns>The names of the first three workbooks in OneDrive for Business.</returns>
        public async Task <JsonResult> OneDriveFiles()
        {
            // Get access token
            var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

            // Get all the Excel files in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
            var fullWorkbooksSearchUrl = GraphApiHelper.GetWorkbookSearchUrl("?$select=name,id&top=3");
            var filesResult            = await ODataHelper.GetItems <ExcelWorkbook>(fullWorkbooksSearchUrl, token.AccessToken);

            List <string> fileNames = new List <string>();

            foreach (ExcelWorkbook workbook in filesResult)
            {
                fileNames.Add(workbook.Name);
            }
            return(Json(fileNames, JsonRequestBehavior.AllowGet));
        }
        public async Task <GraphApiResponseDTO> GetAuthorizedUserDetails(string token)
        {
            GraphApiResponseDTO graphApiResponse = new GraphApiResponseDTO();
            var    client = new GraphApiHelper(token);
            string url    = "https://graph.microsoft.com/v1.0/me";

            using HttpResponseMessage response = await client.ApiClient.GetAsync(url);

            if (response.IsSuccessStatusCode)
            {
                ExternalLoginSsoDTO user = await JsonSerializer.DeserializeAsync <ExternalLoginSsoDTO>(await response.Content.ReadAsStreamAsync());

                graphApiResponse.externalLoginUser = user;
                return(graphApiResponse);
            }
            else
            {
                graphApiResponse.Error = response.ReasonPhrase;
                return(graphApiResponse);
            }
        }
Exemple #17
0
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Console.WriteLine("Running sync");
                BackgroundWorker w = (BackgroundWorker)sender;

                var token = Tokens.getApplicationToken();

                using (TrimHelper trimHelper = new TrimHelper())
                {
                    foreach (var doc in trimHelper.GetDeleteableDocuments())
                    {
                        Console.WriteLine($"rec: {doc.Id}");

                        try
                        {
                            if (!string.IsNullOrWhiteSpace(doc.Id))
                            {
                                var fileResult = ODataHelper.GetItem <OneDriveItem>(GraphApiHelper.GetOneDriveItemIdUrl(doc.Id), token, null);
                                fileResult.Wait();

                                var item = fileResult.Result;

                                var isLocked = ODataHelper.IsLocked(GraphApiHelper.GetOneDriveItemIdUrlForDelete(doc.Id), item.Name, token);
                                isLocked.Wait();

                                if (isLocked.Result == true)
                                {
                                    Console.WriteLine("Item is locked will try again later");
                                }
                                else
                                {
                                    var modified = doc.DateModified.ToUniversalTime();
                                    if (item.LastModifiedDateTime > modified)
                                    {
                                        trimHelper.CheckinFromDrive(doc, token);
                                    }

                                    StringContent content = new StringContent($"[TrimLink]{Environment.NewLine}Uri={doc.Uri}", Encoding.UTF8, "text/plain");
                                    string        url     = GraphApiHelper.GetOneDriveFileUploadUrlFromId(item.ParentReference.DriveId, item.ParentReference.Id, doc.LinkFileName);


                                    // delete original file
                                    var deleteResult = ODataHelper.DeleteWithToken(GraphApiHelper.GetOneDriveItemIdUrlForDelete(doc.Id), token);
                                    deleteResult.Wait();


                                    // Create link in Drive
                                    var uploadResult = ODataHelper.SendRequestWithAccessToken(url, token, content, method: HttpMethod.Put);
                                    uploadResult.Wait();

                                    trimHelper.ClearDriveId(doc);
                                    trimHelper.ResetDeleteNow(doc);

                                    Console.WriteLine(fileResult.Result.ParentReference.Id);
                                }
                            }
                            else
                            {
                                trimHelper.ResetDeleteNow(doc);
                            }
                        }
                        catch (Exception ex)
                        {
                            if (ex.InnerException != null)
                            {
                                Console.WriteLine(ex.InnerException.Message);
                                Console.WriteLine(ex.InnerException.StackTrace);
                            }
                            else
                            {
                                Console.WriteLine(ex.Message);
                                Console.WriteLine(ex.StackTrace);
                            }
                        }
                    }
                }

                //	while (/*condition*/)
                //	{
                //check if cancellation was requested
                if (w.CancellationPending)
                {
                    //take any necessary action upon cancelling (rollback, etc.)

                    //notify the RunWorkerCompleted event handler
                    //that the operation was cancelled
                    e.Cancel = true;
                    return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
            //report progress; this method has an overload which can also take
            //custom object (usually representing state) as an argument
            //	w.ReportProgress(/*percentage*/);

            //do whatever You want the background thread to do...
            //}
        }
Exemple #18
0
        public async Task <Document> GetDocument(string token)
        {
            if (_recordUri < 1)
            {
                throw new ApplicationException("Invalid Uri");
            }

            if (_database == null || !_database.IsConnected)
            {
                throw new ApplicationException("Invalid database");
            }

            var response = new Document()
            {
                UserHasAccess = true
            };
            var record = new Record(_database, _recordUri);

            string driveId = record.GetDriveId();

            if (!string.IsNullOrWhiteSpace(driveId))
            {
                OneDriveItem fileResult = null;

                try
                {
                    fileResult = await ODataHelper.GetItem <OneDriveItem>(GraphApiHelper.GetOneDriveItemIdUrl(driveId), token, null);
                }
                catch (Exception ex)
                {
                    response.UserHasAccess = false;
                }

                if (response.UserHasAccess == false)
                {
                    token      = Tokens.getApplicationToken();
                    fileResult = await ODataHelper.GetItem <OneDriveItem>(GraphApiHelper.GetOneDriveItemIdUrl(driveId), token, null);
                }
                response.WebUrl    = fileResult.WebUrl;
                response.WebDavUrl = fileResult.WebDavUrl;
                response.MimeType  = fileResult.File.MimeType;
            }
            else if (record.IsElectronic)
            {
                try
                {
                    string folderId = string.Empty;

                    var documentFolder = await ODataHelper.PostFolder <OneDriveItem>(GraphApiHelper.GetOneDriveChildrenUrl(), token);

                    folderId = documentFolder.Id;

                    if (!record.IsDocumentInClientCache)
                    {
                        record.LoadDocumentIntoClientCache();
                    }

                    string fileName = record.GetFileName();

                    var uploadedFile = await doUpload(record.DocumentPathInClientCache, fileName, token);

                    bool checkout = true;
                    if (record.IsCheckedOut && record.CheckedOutTo.Uri == _database.CurrentUser.Uri)
                    {
                        checkout = false;
                    }


                    record.GetDocument(null, checkout, null, uploadedFile.ParentReference.DriveId + "/items/" + uploadedFile.Id);
                    record.SetDriveId(uploadedFile.ParentReference.DriveId + "/items/" + uploadedFile.Id);                    // uploadedFile. fileItem.getDriveAndId();

                    record.Save();


                    response.WebUrl    = uploadedFile.WebUrl;
                    response.WebDavUrl = uploadedFile.WebDavUrl;
                }
                catch
                {
                    try
                    {
                        record.UndoCheckout(null);
                    }
                    catch { }
                    //	return new Error
                    throw;
                }
            }
            else
            {
                throw new Exception("Record is not a valid document.");
            }

            return(response);
        }
Exemple #19
0
        private async Task <Microsoft.Graph.DriveItem> doUpload(string filePath, string fileName, string token)
        {
            var graphServiceClient = getClient(token);


            string userFolder = Path.Combine("ForUser", _database.CurrentUser.Uri.ToString());

            string fullUserFolder = Path.Combine(_uploadBasePath, userFolder);

            //string fileName = $"{Guid.NewGuid()}.docx";

            if (!System.IO.Directory.Exists(fullUserFolder))
            {
                System.IO.Directory.CreateDirectory(fullUserFolder);
            }

            string tempPath = Path.Combine(fullUserFolder, Path.GetFileName(filePath));

            System.IO.File.Copy(filePath, tempPath, true);

            FileInfo fileInfo = new FileInfo(tempPath);

            fileInfo.IsReadOnly = false;

            autoOpen(tempPath);


            //	autoOpen(tempPath);
            using (var file = System.IO.File.OpenRead(tempPath))
            {
                var documentFolder = await ODataHelper.PostFolder <OneDriveItem>(GraphApiHelper.GetOneDriveChildrenUrl(), token);


                var uploadSession = await graphServiceClient.Drives[documentFolder.ParentReference.DriveId].Items[documentFolder.Id].ItemWithPath(fileName).CreateUploadSession().Request().PostAsync();

                string ul = uploadSession.UploadUrl += "&$select=Id,ParentReference,WebUrl,WebDavUrl";

                var maxChunkSize = (320 * 1024) * 10;                 // 5000 KB - Change this to your chunk size. 5MB is the default.
                var provider     = new ChunkedUploadProvider(uploadSession, graphServiceClient, file, maxChunkSize);

                try
                {
                    // Setup the chunk request necessities
                    var       chunkRequests     = provider.GetUploadChunkRequests();
                    var       readBuffer        = new byte[maxChunkSize];
                    var       trackedExceptions = new List <Exception>();
                    DriveItem itemResult        = null;

                    //upload the chunks
                    foreach (var request in chunkRequests)
                    {
                        // Do your updates here: update progress bar, etc.
                        // ...
                        // Send chunk request
                        var result = await provider.GetChunkRequestResponseAsync(request, readBuffer, trackedExceptions);

                        if (result.UploadSucceeded)
                        {
                            itemResult = result.ItemResponse;
                        }
                    }

                    // Check that upload succeeded
                    if (itemResult != null)
                    {
                        return(itemResult);
                    }
                }
                catch
                {
                    await provider.DeleteSession();

                    throw;
                }
            }

            System.IO.File.Delete(tempPath);
            throw new ApplicationException("Upload failed.");
        }
        // GET api/files
        public async Task <HttpResponseMessage> Get()
        {
            string accessToken = Request.Headers.Authorization.ToString().Split(' ')[1];

            return(await GraphApiHelper.GetOneDriveFileNames(accessToken));
        }
Exemple #21
0
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            Dictionary <string, string> errorObj = new Dictionary <string, string>();

            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;

                // Get the access token for MS Graph.
                string[] graphScopes = { "Files.Read.All" };

                GraphToken result = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await GraphTokenHelper.AcquireTokenOnBehalfOfAsync(bootstrapContext.Token, graphScopes);
                }
                catch (GraphTokenException e)
                {
                    // Even we have the access token from Office, it's possible the Azure AD fails the request.
                    // Potential reasons could be: 1) Require MFA, 2) Missing consent
                    // Why missing consent could happen?
                    // To get an access token from Office client, user only needs to grant the "access_as_user" scope.
                    // It doesnt mean user has granted all the required scopes.
                    // If the required scopes have updated (for example, the developer change the scopes in http://apps.dev.microsoft.com/,
                    // user has to consent again for those new scopes.
                    // Therefore, the server should send the error to the client and let the client to handle it.
                    errorObj["claims"]    = e.Claims;
                    errorObj["message"]   = e.Message;
                    errorObj["errorCode"] = e.ErrorCode;
                    errorObj["suberror"]  = e.SubError;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }
                catch (Exception e)
                {
                    errorObj["errorCode"] = "unknown_error";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.InternalServerError, errorObj));
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");

                IEnumerable <OneDriveItem> filesResult;
                try
                {
                    filesResult = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);
                }

                // If the token is invalid, MS Graph sends a "401 Unauthorized" error with the code
                // "InvalidAuthenticationToken". ASP.NET then throws a RuntimeBinderException. This
                // is also what happens when the token is expired, although MSAL should prevent that
                // from ever happening. In either case, the client should start the process over by
                // re-calling getAccessTokenAsync.
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
                {
                    errorObj["errorCode"] = "invalid_graph_token";
                    errorObj["message"]   = e.Message;
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
                }

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in filesResult)
                {
                    itemNames.Add(item.Name);
                }

                var requestMessage = new HttpRequestMessage();
                requestMessage.SetConfiguration(new HttpConfiguration());
                var response = requestMessage.CreateResponse <List <string> >(HttpStatusCode.OK, itemNames);
                return(response);
            }
            else
            {
                // The token from the client does not have "access_as_user" permission.
                errorObj["errorCode"] = "invalid_access_token";
                errorObj["message"]   = "Missing access_as_user. Microsoft Office does not have permission to get Microsoft Graph data on behalf of the current user.";
                return(SendErrorToClient(HttpStatusCode.Unauthorized, errorObj));
            }
        }
Exemple #22
0
        public async Task <string> SaveAttachmentOneDrive([FromBody] SaveAttachmentRequest request)
        {
            if (request == null || !request.IsValid() || request.attachmentIds.Length == 0)
            {
                return(null);
            }

            string attachmentsUrl = null;

            using (var client = new HttpClient())
            {
                // Get content bytes
                string baseAttachmentUri = request.outlookRestUrl;
                if (!baseAttachmentUri.EndsWith("/"))
                {
                    baseAttachmentUri += "/";
                }
                baseAttachmentUri += "v2.0/me/messages/" + request.messageId + "/attachments/";

                var i = 0;
                foreach (string attachmentId in request.attachmentIds)
                {
                    var getAttachmentReq = new HttpRequestMessage(HttpMethod.Get, baseAttachmentUri + attachmentId);

                    // Headers
                    getAttachmentReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", request.outlookToken);
                    getAttachmentReq.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    var result = await client.SendAsync(getAttachmentReq);

                    string json = await result.Content.ReadAsStringAsync();

                    OutlookAttachment attachment = JsonConvert.DeserializeObject <OutlookAttachment>(json);

                    // For files, build a stream directly from ContentBytes
                    if (attachment.Size < (4 * 1024 * 1024))
                    {
                        MemoryStream fileStream = new MemoryStream(Convert.FromBase64String(attachment.ContentBytes));


                        // Get access token from SQL database
                        var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

                        // TODO: Check if the file already exists

                        attachmentsUrl = await GraphApiHelper.saveAttachmentOneDrive(token.AccessToken, Format.MakeFileNameValid(request.filenames[i]), fileStream, Format.MakeFileNameValid(request.subject));

                        // Format
                        string delete = "/" + request.filenames[i];
                        attachmentsUrl = attachmentsUrl.Replace(delete, "");

                        i++;
                    }
                    else
                    {
                        // Functionality to support > 4 MB files
                        // See https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0
                        var token = Data.GetUserSessionToken(Settings.GetUserAuthStateId(ControllerContext.HttpContext), Settings.AzureADAuthority);

                        DriveItem folder = await GraphApiHelper.searchFileOneDrive(token.AccessToken, "Outlook Attachments");


                        var url       = "https://graph.microsoft.com/v1.0" + $"/me/drive/items/{folder.Id}:/{attachment.Name}:/createUploadSession";
                        var uploadReq = new HttpRequestMessage(HttpMethod.Post, url);


                        // Headers
                        uploadReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.AccessToken);

                        // Send request
                        var sessionResponse = client.SendAsync(uploadReq).Result.Content.ReadAsStringAsync().Result;


                        var uploadSession = JsonConvert.DeserializeObject <UploadSessionResponse>(sessionResponse);

                        Upload upload = new Upload();

                        HttpResponseMessage response = upload.UploadFileBySession(uploadSession.uploadUrl, Convert.FromBase64String(attachment.ContentBytes));


                        return(folder.WebUrl);
                    }
                }

                return(attachmentsUrl);
            }
        }
        // GET api/values
        public async Task <HttpResponseMessage> Get()
        {
            // OWIN middleware validated the audience and issuer, but the scope must also be validated; must contain "access_as_user".
            string[] addinScopes = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value.Split(' ');
            if (addinScopes.Contains("access_as_user"))
            {
                // Get the raw token that the add-in page received from the Office host.
                var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext
                                       as BootstrapContext;
                UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token);

                // Get the access token for MS Graph.
                ClientCredential clientCred       = new ClientCredential(ConfigurationManager.AppSettings["ida:Password"]);
                ConfidentialClientApplication cca =
                    new ConfidentialClientApplication(ConfigurationManager.AppSettings["ida:ClientID"],
                                                      "https://localhost:44355", clientCred, null, null);
                string[]             graphScopes = { "Files.Read.All" };
                AuthenticationResult result      = null;
                try
                {
                    // The AcquireTokenOnBehalfOfAsync method will first look in the MSAL in memory cache for a
                    // matching access token. Only if there isn't one, does it initiate the "on behalf of" flow
                    // with the Azure AD V2 endpoint.
                    result = await cca.AcquireTokenOnBehalfOfAsync(graphScopes, userAssertion, "https://login.microsoftonline.com/common/oauth2/v2.0");
                }
                catch (MsalServiceException e)
                {
                    // If multi-factor authentication is required by the MS Graph resource and the user
                    // has not yet provided it, AAD will return "400 Bad Request" with error AADSTS50076
                    // and a Claims property. MSAL throws a MsalUiRequiredException (which inherits
                    // from MsalServiceException) with this information. The Claims property value must
                    // be passed to the client which should pass it to the Office host, which then
                    // includes it in a request for a new token. AAD will prompt the user for all
                    // required forms of authentication.
                    if (e.Message.StartsWith("AADSTS50076"))
                    {
                        // The APIs that create HTTP Responses from exceptions don't know about the
                        // Claims property, so they don't include it. We have to manually create a message
                        // that includes it. A custom Message property, however, blocks the creation of an
                        // ExceptionMessage property, so the only way to get the error AADSTS50076 to the
                        // client is to add it to the custom Message. JavaScript in the client will need
                        // to discover if a response has a Message or ExceptionMessage, so it knows which
                        // to read.
                        string responseMessage = String.Format("{{\"AADError\":\"AADSTS50076\",\"Claims\":{0}}}", e.Claims);
                        return(SendErrorToClient(HttpStatusCode.Forbidden, null, responseMessage));
                    }

                    // If the call to AAD contained at least one scope (permission) for which neither
                    // the user nor a tenant administrator has consented (or consent was revoked.
                    // AAD will return "400 Bad Request" with error AADSTS65001. MSAL throws a
                    // MsalUiRequiredException with this information. The client should re-call
                    // getAccessTokenAsync with the option { forceConsent: true }.
                    if ((e.Message.StartsWith("AADSTS65001"))

                        // If the call to AAD contained at least one scope that AAD does not recognize,
                        // AAD returns "400 Bad Request" with error AADSTS70011. MSAL throws a
                        // MsalUiRequiredException (which inherits from MsalServiceException) with this
                        // information. The client should inform the user.
                        || (e.Message.StartsWith("AADSTS70011: The provided value for the input parameter 'scope' is not valid.")))
                    {
                        return(SendErrorToClient(HttpStatusCode.Forbidden, e, null));
                    }
                    else
                    {
                        // Rethrowing the MsalServiceException will not relay the original
                        // "400 Bad Request" exception to the client. Instead a "500 Server Error"
                        // is sent.
                        throw e;
                    }
                }

                // Get the names of files and folders in OneDrive for Business by using the Microsoft Graph API. Select only properties needed.
                // Note that the parameter is hardcoded. If you reuse this code in a production add-in and any part of the query parameter comes
                // from user input, be sure that it is sanitized so that it cannot be used in a Response header injection attack.
                var fullOneDriveItemsUrl = GraphApiHelper.GetOneDriveItemNamesUrl("?$select=name&$top=3");

                IEnumerable <OneDriveItem> filesResult;
                try
                {
                    filesResult = await ODataHelper.GetItems <OneDriveItem>(fullOneDriveItemsUrl, result.AccessToken);
                }

                // If the token is invalid, MS Graph sends a "401 Unauthorized" error with the code
                // "InvalidAuthenticationToken". ASP.NET then throws a RuntimeBinderException. This
                // is also what happens when the token is expired, although MSAL should prevent that
                // from ever happening. In either case, the client should start the process over by
                // re-calling getAccessTokenAsync.
                catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)
                {
                    return(SendErrorToClient(HttpStatusCode.Unauthorized, e, null));
                }

                // The returned JSON includes OData metadata and eTags that the add-in does not use.
                // Return to the client-side only the filenames.
                List <string> itemNames = new List <string>();
                foreach (OneDriveItem item in filesResult)
                {
                    itemNames.Add(item.Name);
                }

                var requestMessage = new HttpRequestMessage();
                requestMessage.SetConfiguration(new HttpConfiguration());
                var response = requestMessage.CreateResponse <List <string> >(HttpStatusCode.OK, itemNames);
                return(response);
            }
            // The token from the client does not have "access_as_user" permission.
            return(SendErrorToClient(HttpStatusCode.Unauthorized, null, "Missing access_as_user."));
        }