Class handles token generation and validation for the WOPI host
        /// <summary>
        /// Determines if the user is authorized to access the WebAPI endpoint based on the bearer token
        /// </summary>
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            try
            {
                // Parse the query string and ensure there is an access_token
                var queryParams = parseRequestParams(actionContext.Request.RequestUri.Query);
                if (!queryParams.ContainsKey("access_token"))
                {
                    return(false);
                }

                // Get the details of the WOPI request
                WopiRequest requestData = new WopiRequest()
                {
                    RequestType = WopiRequestType.None,
                    AccessToken = queryParams["access_token"],
                    Id          = actionContext.RequestContext.RouteData.Values["id"].ToString()
                };

                // Get the requested file from Document DB
                var itemId = new Guid(requestData.Id);
                var file   = DocumentRepository <DetailedFileModel> .GetItem("Files", i => i.id == itemId);

                // Check for missing file
                if (file == null)
                {
                    return(false);
                }

                // Validate the access token
                return(WopiSecurity.ValidateToken(requestData.AccessToken, file.Container, file.id.ToString()));
            }
            catch (Exception)
            {
                // Any exception will return false, but should probably return an alternate status codes
                return(false);
            }
        }
Beispiel #2
0
        public async Task<ActionResult> Detail(Guid id)
        {
            // Make sure an action was passed in
            if (String.IsNullOrEmpty(Request["action"]))
                return RedirectToAction("Error", "Home", new { error = "No action provided" });

            // Get the specific file from DocumentDB
            var file = DocumentDBRepository<FileModel>.GetItem("Files",
                i => i.OwnerId == User.Identity.Name.ToLower() && i.id == id);

            // Check for null file
            if (file == null)
                return RedirectToAction("Error", "Home", new { error = "Files does not exist" });

            // Use discovery to determine endpoint to leverage
            List<WopiAction> discoData = await WopiUtil.GetDiscoveryInfo();
            var fileExt = file.BaseFileName.Substring(file.BaseFileName.LastIndexOf('.') + 1).ToLower();
            var action = discoData.FirstOrDefault(i => i.name == Request["action"] && i.ext == fileExt);

            // Make sure the action isn't null
            if (action != null)
            {
                string urlsrc = WopiUtil.GetActionUrl(action, file, Request.Url.Authority);

                // Generate JWT token for the user/document
                WopiSecurity wopiSecurity = new WopiSecurity();
                var token = wopiSecurity.GenerateToken(User.Identity.Name.ToLower(), getUserContainer(), id.ToString());
                ViewData["access_token"] = wopiSecurity.WriteToken(token);
                ViewData["access_token_ttl"] = token.ValidTo.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
                ViewData["wopi_urlsrc"] = urlsrc;
                return View();
            }
            else
            {
                // This will only hit if the extension isn't supported by WOPI
                return RedirectToAction("Error", "Home", new { error = "File is not a supported WOPI extension" });
            }
        }
Beispiel #3
0
        /// <summary>
        /// Processes a PutRelativeFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on PutRelativeFile, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/PutRelativeFile.html
        /// </remarks>
        private async static Task<HttpResponseMessage> PutRelativeFile(this HttpContext context, DetailedFileModel file, List<WopiAction> actions)
        {
            // Determine the specific mode
            if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null &&
                context.Request.Headers[WopiRequestHeaders.SUGGESTED_TARGET] != null)
            {
                // Theses headers are mutually exclusive, so we should return a 501 Not Implemented
                return returnStatus(HttpStatusCode.NotImplemented, "Both RELATIVE_TARGET and SUGGESTED_TARGET were present");
            }
            else if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null ||
                context.Request.Headers[WopiRequestHeaders.SUGGESTED_TARGET] != null)
            {
                string fileName = "";
                if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null)
                {
                    // Specific mode...use the exact filename
                    fileName = context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET];
                }
                else
                {
                    // Suggested mode...might just be an extension
                    fileName = context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET];
                    if (fileName.IndexOf('.') == 0)
                        fileName = file.BaseFileName.Substring(0, file.BaseFileName.LastIndexOf('.')) + fileName;
                }

                // Create the file entity
                DetailedFileModel newFile = new DetailedFileModel()
                {
                    id = Guid.NewGuid(),
                    OwnerId = file.OwnerId,
                    BaseFileName = fileName,
                    Size = context.Request.InputStream.Length,
                    Container = file.Container,
                    Version = 1
                };

                // First stream the file into blob storage
                var stream = context.Request.InputStream;
                var bytes = new byte[stream.Length];
                await stream.ReadAsync(bytes, 0, (int)stream.Length);
                var id = await Utils.AzureStorageUtil.UploadFile(newFile.id.ToString(), newFile.Container, bytes);

                // Write the details into documentDB
                await DocumentDBRepository<FileModel>.CreateItemAsync("Files", (FileModel)newFile);

                // Get access token for the new file
                WopiSecurity security = new WopiSecurity();
                var token = security.GenerateToken(newFile.OwnerId, newFile.Container, newFile.id.ToString());
                var tokenStr = security.WriteToken(token);

                // Prepare the Json response
                string json = String.Format("{ 'Name': '{0}, 'Url': 'https://{1}/wopi/files/{2}?access_token={3}'",
                    newFile.BaseFileName, context.Request.Url.Authority, newFile.id.ToString(), tokenStr);

                // Add the optional properties to response if applicable (HostViewUrl, HostEditUrl)
                var fileExt = newFile.BaseFileName.Substring(newFile.BaseFileName.LastIndexOf('.') + 1).ToLower();
                var view = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "view");
                if (view != null)
                    json += String.Format(", 'HostViewUrl': '{0}'", WopiUtil.GetActionUrl(view, newFile, context.Request.Url.Authority));
                var edit = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "edit");
                if (edit != null)
                    json += String.Format(", 'HostEditUrl': '{0}'", WopiUtil.GetActionUrl(edit, newFile, context.Request.Url.Authority));
                json += " }";

                // Write the response and return a success 200
                var response = returnStatus(HttpStatusCode.OK, "Success");
                response.Content = new StringContent(json);
                return response;
            }
            else
            {
                return returnStatus(HttpStatusCode.BadRequest, "PutRelativeFile mode was not provided in the request");
            }
        }