/// <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); } }
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" }); } }
/// <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"); } }