petrsvihlik/WopiHost

View on GitHub
src/WopiHost.Core/Controllers/WopiBootstrapperController.cs

Summary

Maintainability
A
2 hrs
Test Coverage
using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using WopiHost.Abstractions;
using WopiHost.Core.Extensions;
using WopiHost.Core.Infrastructure;
using WopiHost.Core.Models;
using WopiHost.Core.Results;
 
namespace WopiHost.Core.Controllers;
 
/// <summary>
/// Controller containing the bootstrap operation.
/// </summary>
/// <remarks>
/// Creates an instance of <see cref="WopiBootstrapperController"/>.
/// </remarks>
/// <param name="storageProvider">Storage provider instance for retrieving files and folders.</param>
/// <param name="securityHandler">Security handler instance for performing security-related operations.</param>
[Authorize]
[ApiController]
[Route("wopibootstrapper")]
public class WopiBootstrapperController(
IWopiStorageProvider storageProvider,
IWopiSecurityHandler securityHandler) : ControllerBase
{
/// <summary>
/// Gets information about the root container.
/// </summary>
/// <returns></returns>
Method `GetRootContainer` has 52 lines of code (exceeds 25 allowed). Consider refactoring.
[HttpPost]
[Produces(MediaTypeNames.Application.Json)]
public async Task<IActionResult> GetRootContainer(
[FromHeader(Name = WopiHeaders.ECOSYSTEM_OPERATION)] string? ecosystemOperation = null,
[FromHeader(Name = WopiHeaders.WOPI_SRC)] string? wopiSrc = null,
CancellationToken cancellationToken = default)
//TODO: fix the path
{
var authorizationHeader = HttpContext.Request.Headers.Authorization;
if (ValidateAuthorizationHeader(authorizationHeader))
{
//TODO: supply user
var user = "Anonymous";
 
//TODO: implement bootstrap
var bootstrapRoot = new BootstrapRootContainerInfo
{
Bootstrap = new BootstrapInfo
{
EcosystemUrl = Url.GetWopiUrl(WopiRouteNames.CheckEcosystem),
SignInName = "",
UserFriendlyName = "",
UserId = ""
}
};
if (ecosystemOperation == "GET_ROOT_CONTAINER")
{
var resourceId = storageProvider.RootContainerPointer.Identifier;
var token = await securityHandler.GenerateAccessToken(user, resourceId, cancellationToken);
 
bootstrapRoot.RootContainerInfo = new RootContainerInfo
{
ContainerPointer = new ChildContainer(
storageProvider.RootContainerPointer.Name,
Url.GetWopiUrl(WopiResourceType.Container, storageProvider.RootContainerPointer.Identifier, securityHandler.WriteToken(token)))
};
}
else if (ecosystemOperation == "GET_NEW_ACCESS_TOKEN")
{
ArgumentException.ThrowIfNullOrEmpty(wopiSrc);
var token = await securityHandler.GenerateAccessToken(user, GetIdFromUrl(wopiSrc), cancellationToken);
 
bootstrapRoot.AccessTokenInfo = new AccessTokenInfo
{
AccessToken = securityHandler.WriteToken(token),
AccessTokenExpiry = token.ValidTo.ToUnixTimestamp()
};
}
else
{
return new NotImplementedResult();
}
return new JsonResult(bootstrapRoot);
}
else
{
//TODO: implement WWW-authentication header https://learn.microsoft.com/microsoft-365/cloud-storage-partner-program/rest/bootstrapper/bootstrap#www-authenticate-response-header-format
var authorizationUri = "https://contoso.com/api/oauth2/authorize";
var tokenIssuanceUri = "https://contoso.com/api/oauth2/token";
var providerId = "tp_contoso";
var urlSchemes = Uri.EscapeDataString("{\"iOS\" : [\"contoso\",\"contoso - EMM\"], \"Android\" : [\"contoso\",\"contoso - EMM\"], \"UWP\": [\"contoso\",\"contoso - EMM\"]}");
Response.Headers.Append("WWW-Authenticate", $"Bearer authorization_uri=\"{authorizationUri}\",tokenIssuance_uri=\"{tokenIssuanceUri}\",providerId=\"{providerId}\", UrlSchemes=\"{urlSchemes}\"");
return new UnauthorizedResult();
}
}
 
private static string GetIdFromUrl(string resourceUrl)
{
var resourceId = resourceUrl[(resourceUrl.LastIndexOf('/') + 1)..];
var queryIndex = resourceId.IndexOf('?');
if (queryIndex > -1)
{
resourceId = resourceId[..queryIndex];
}
resourceId = Uri.UnescapeDataString(resourceId);
return resourceId;
}
 
private static bool ValidateAuthorizationHeader(StringValues authorizationHeader)
{
//TODO: implement header validation https://learn.microsoft.com/microsoft-365/cloud-storage-partner-program/rest/bootstrapper/getrootcontainer#sample-response
// http://stackoverflow.com/questions/31948426/oauth-bearer-token-authentication-is-not-passing-signature-validation
return true;
}
}