diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml index 82627e1..871f959 100644 --- a/.github/workflows/integrate.yml +++ b/.github/workflows/integrate.yml @@ -14,7 +14,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Restore dependencies run: dotnet restore -s ${MYGET_FEED} -s https://api.nuget.org/v3/index.json env: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 2a64596..6014382 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Restore dependencies run: dotnet restore -s ${MYGET_FEED} -s https://api.nuget.org/v3/index.json env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a141787..b7260b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Extract version from tag id: get_version uses: battila7/get-version-action@v2 diff --git a/src/WopiHost.Abstractions/WopiAuthorizationRequirement.cs b/src/WopiHost.Abstractions/WopiAuthorizationRequirement.cs index a0e88fa..7288318 100644 --- a/src/WopiHost.Abstractions/WopiAuthorizationRequirement.cs +++ b/src/WopiHost.Abstractions/WopiAuthorizationRequirement.cs @@ -5,19 +5,14 @@ namespace WopiHost.Abstractions; /// /// Represents an authorization requirement for a given combination of resource, user, and action. /// -public class WopiAuthorizationRequirement : IAuthorizationRequirement +/// +/// Creates an instance of initialized with . +/// +/// Permissions required for a given combination of resource, user, and action. +public class WopiAuthorizationRequirement(Permission permission) : IAuthorizationRequirement { /// /// Gets a permissions required for a given combination of resource, user, and action. /// - public Permission Permission { get; } - - /// - /// Creates an instance of initialized with . - /// - /// Permissions required for a given combination of resource, user, and action. - public WopiAuthorizationRequirement(Permission permission) - { - Permission = permission; - } + public Permission Permission { get; } = permission; } diff --git a/src/WopiHost.Abstractions/WopiHost.Abstractions.csproj b/src/WopiHost.Abstractions/WopiHost.Abstractions.csproj index 86c44a5..5d26204 100644 --- a/src/WopiHost.Abstractions/WopiHost.Abstractions.csproj +++ b/src/WopiHost.Abstractions/WopiHost.Abstractions.csproj @@ -3,7 +3,7 @@ WopiHost.Abstractions Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.Abstractions WopiHost.Abstractions LICENSE.txt @@ -33,13 +33,13 @@ - + - + - + diff --git a/src/WopiHost.Cobalt/CobaltHostLockingStore.cs b/src/WopiHost.Cobalt/CobaltHostLockingStore.cs index 2148029..2db9121 100644 --- a/src/WopiHost.Cobalt/CobaltHostLockingStore.cs +++ b/src/WopiHost.Cobalt/CobaltHostLockingStore.cs @@ -3,14 +3,9 @@ namespace WopiHost.Cobalt; -public class CobaltHostLockingStore : HostLockingStore +public class CobaltHostLockingStore(ClaimsPrincipal principal) : HostLockingStore { - private readonly ClaimsPrincipal _principal; - - public CobaltHostLockingStore(ClaimsPrincipal principal) - { - _principal = principal; - } + private readonly ClaimsPrincipal _principal = principal; public override WhoAmIRequest.OutputType HandleWhoAmI(WhoAmIRequest.InputType input) { @@ -180,10 +175,7 @@ public override GetCoauthoringStatusRequest.OutputType HandleGetCoauthoringStatu return result; } - public override Dictionary QueryEditorsTable() - { - return new Dictionary(); - } + public override Dictionary QueryEditorsTable() => new Dictionary(); public override JoinEditingSessionRequest.OutputType HandleJoinEditingSession(JoinEditingSessionRequest.InputType input) { @@ -220,10 +212,7 @@ public override RemoveEditorMetadataRequest.OutputType HandleRemoveEditorMetadat return result; } - public override ulong GetEditorsTableWaterline() - { - return 0; - } + public override ulong GetEditorsTableWaterline() => 0; public override AmIAloneRequest.OutputType HandleAmIAlone(AmIAloneRequest.InputType input) { diff --git a/src/WopiHost.Cobalt/WopiHost.Cobalt.csproj b/src/WopiHost.Cobalt/WopiHost.Cobalt.csproj index 0606ff4..15f515c 100644 --- a/src/WopiHost.Cobalt/WopiHost.Cobalt.csproj +++ b/src/WopiHost.Cobalt/WopiHost.Cobalt.csproj @@ -3,7 +3,7 @@ WopiHost.Cobalt Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.Cobalt WopiHost.Cobalt LICENSE.txt @@ -30,7 +30,7 @@ - + diff --git a/src/WopiHost.Core/Controllers/ContainersController.cs b/src/WopiHost.Core/Controllers/ContainersController.cs index 7d0da8e..2b43fb7 100644 --- a/src/WopiHost.Core/Controllers/ContainersController.cs +++ b/src/WopiHost.Core/Controllers/ContainersController.cs @@ -10,18 +10,15 @@ namespace WopiHost.Core.Controllers; /// /// Implementation of WOPI server protocol https://msdn.microsoft.com/en-us/library/hh659001.aspx /// +/// +/// Creates an instance of . +/// +/// Storage provider instance for retrieving files and folders. +/// Security handler instance for performing security-related operations. +/// WOPI Host configuration [Route("wopi/[controller]")] -public class ContainersController : WopiControllerBase +public class ContainersController(IOptionsSnapshot wopiHostOptions, IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler) : WopiControllerBase(storageProvider, securityHandler, wopiHostOptions) { - /// - /// Creates an instance of . - /// - /// Storage provider instance for retrieving files and folders. - /// Security handler instance for performing security-related operations. - /// WOPI Host configuration - public ContainersController(IOptionsSnapshot wopiHostOptions, IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler) : base(storageProvider, securityHandler, wopiHostOptions) - { - } /// /// Returns the metadata about a container specified by an identifier. diff --git a/src/WopiHost.Core/Controllers/EcosystemController.cs b/src/WopiHost.Core/Controllers/EcosystemController.cs index 9caa7f7..79f8875 100644 --- a/src/WopiHost.Core/Controllers/EcosystemController.cs +++ b/src/WopiHost.Core/Controllers/EcosystemController.cs @@ -9,27 +9,23 @@ namespace WopiHost.Core.Controllers; /// /// Implementation of WOPI server protocol http://wopi.readthedocs.io/projects/wopirest/en/latest/ecosystem/CheckEcosystem.html /// +/// +/// Creates an instance of . +/// +/// Storage provider instance for retrieving files and folders. +/// Security handler instance for performing security-related operations. +/// WOPI Host configuration [Route("wopi/[controller]")] - public class EcosystemController : WopiControllerBase + public class EcosystemController(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) : WopiControllerBase(storageProvider, securityHandler, wopiHostOptions) { - /// - /// Creates an instance of . - /// - /// Storage provider instance for retrieving files and folders. - /// Security handler instance for performing security-related operations. - /// WOPI Host configuration - public EcosystemController(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) - : base(storageProvider, securityHandler, wopiHostOptions) - { - } - /// - /// The GetRootContainer operation returns the root container. A WOPI client can use this operation to get a reference to the root container, from which the client can call EnumerateChildren (containers) to navigate a container hierarchy. - /// Specification: http://wopi.readthedocs.io/projects/wopirest/en/latest/ecosystem/GetRootContainer.html - /// Example URL: GET /wopi/ecosystem/root_container_pointer - /// - /// - [HttpGet("root_container_pointer")] + /// + /// The GetRootContainer operation returns the root container. A WOPI client can use this operation to get a reference to the root container, from which the client can call EnumerateChildren (containers) to navigate a container hierarchy. + /// Specification: http://wopi.readthedocs.io/projects/wopirest/en/latest/ecosystem/GetRootContainer.html + /// Example URL: GET /wopi/ecosystem/root_container_pointer + /// + /// + [HttpGet("root_container_pointer")] [Produces(MediaTypeNames.Application.Json)] public RootContainerInfo GetRootContainer() //TODO: fix the path { diff --git a/src/WopiHost.Core/Controllers/FilesController.cs b/src/WopiHost.Core/Controllers/FilesController.cs index 97eb33b..31425c2 100644 --- a/src/WopiHost.Core/Controllers/FilesController.cs +++ b/src/WopiHost.Core/Controllers/FilesController.cs @@ -155,13 +155,9 @@ public async Task PutFile(string id) /// /// File identifier. /// Returns if succeeded. - [HttpPost("{id}"), WopiOverrideHeader(new[] { "PUT_RELATIVE" })] + [HttpPost("{id}"), WopiOverrideHeader(["PUT_RELATIVE"])] #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously - public async Task PutRelativeFile(string id) -#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously - { - throw new NotImplementedException($"{nameof(PutRelativeFile)} is not implemented yet."); - } + public async Task PutRelativeFile(string id) => throw new NotImplementedException($"{nameof(PutRelativeFile)} is not implemented yet."); /// /// Changes the contents of the file in accordance with [MS-FSSHTTP]. @@ -170,7 +166,7 @@ public async Task PutRelativeFile(string id) /// Example URL path: /wopi/files/(file_id) /// /// File identifier. - [HttpPost("{id}"), WopiOverrideHeader(new[] { "COBALT" })] + [HttpPost("{id}"), WopiOverrideHeader(["COBALT"])] public async Task ProcessCobalt(string id) { // Check permissions @@ -189,8 +185,8 @@ public async Task ProcessCobalt(string id) } var responseAction = CobaltProcessor.ProcessCobalt(file, User, await HttpContext.Request.Body.ReadBytesAsync()); - HttpContext.Response.Headers.Add(WopiHeaders.CORRELATION_ID, HttpContext.Request.Headers[WopiHeaders.CORRELATION_ID]); - HttpContext.Response.Headers.Add("request-id", HttpContext.Request.Headers[WopiHeaders.CORRELATION_ID]); + HttpContext.Response.Headers.Append(WopiHeaders.CORRELATION_ID, HttpContext.Request.Headers[WopiHeaders.CORRELATION_ID]); + HttpContext.Response.Headers.Append("request-id", HttpContext.Request.Headers[WopiHeaders.CORRELATION_ID]); return new Results.FileResult(responseAction, "application/octet-stream"); } @@ -202,7 +198,7 @@ public async Task ProcessCobalt(string id) /// Example URL path: /wopi/files/(file_id) /// /// File identifier. - [HttpPost("{id}"), WopiOverrideHeader(new[] { "LOCK", "UNLOCK", "REFRESH_LOCK", "GET_LOCK" })] + [HttpPost("{id}"), WopiOverrideHeader(["LOCK", "UNLOCK", "REFRESH_LOCK", "GET_LOCK"])] public IActionResult ProcessLock(string id) { string oldLock = Request.Headers[WopiHeaders.OLD_LOCK]; diff --git a/src/WopiHost.Core/Controllers/WopiBootstrapperController.cs b/src/WopiHost.Core/Controllers/WopiBootstrapperController.cs index 6d842d6..2d3eaa4 100644 --- a/src/WopiHost.Core/Controllers/WopiBootstrapperController.cs +++ b/src/WopiHost.Core/Controllers/WopiBootstrapperController.cs @@ -1,4 +1,5 @@ using System.Net.Mime; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -11,19 +12,15 @@ namespace WopiHost.Core.Controllers; /// /// Controller containing the bootstrap operation. /// +/// +/// Creates an instance of . +/// +/// Storage provider instance for retrieving files and folders. +/// Security handler instance for performing security-related operations. +/// WOPI Host configuration [Route("wopibootstrapper")] -public class WopiBootstrapperController : WopiControllerBase +public class WopiBootstrapperController(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) : WopiControllerBase(storageProvider, securityHandler, wopiHostOptions) { - /// - /// Creates an instance of . - /// - /// Storage provider instance for retrieving files and folders. - /// Security handler instance for performing security-related operations. - /// WOPI Host configuration - public WopiBootstrapperController(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) - : base(storageProvider, securityHandler, wopiHostOptions) - { - } /// /// Gets information about the root container. @@ -33,7 +30,7 @@ public WopiBootstrapperController(IWopiStorageProvider storageProvider, IWopiSec [Produces(MediaTypeNames.Application.Json)] public IActionResult GetRootContainer() //TODO: fix the path { - var authorizationHeader = HttpContext.Request.Headers["Authorization"]; + var authorizationHeader = HttpContext.Request.Headers.Authorization; var ecosystemOperation = HttpContext.Request.Headers[WopiHeaders.ECOSYSTEM_OPERATION]; var wopiSrc = HttpContext.Request.Headers[WopiHeaders.WOPI_SRC].FirstOrDefault(); @@ -90,27 +87,25 @@ public IActionResult GetRootContainer() //TODO: fix the path 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.Add("WWW-Authenticate", $"Bearer authorization_uri=\"{authorizationUri}\",tokenIssuance_uri=\"{tokenIssuanceUri}\",providerId=\"{providerId}\", UrlSchemes=\"{urlSchemes}\""); + Response.Headers.Append("WWW-Authenticate", $"Bearer authorization_uri=\"{authorizationUri}\",tokenIssuance_uri=\"{tokenIssuanceUri}\",providerId=\"{providerId}\", UrlSchemes=\"{urlSchemes}\""); return new UnauthorizedResult(); } } private string GetIdFromUrl(string resourceUrl) { - var resourceId = resourceUrl[(resourceUrl.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; - var queryIndex = resourceId.IndexOf("?", StringComparison.Ordinal); + var resourceId = resourceUrl[(resourceUrl.LastIndexOf('/') + 1)..]; + var queryIndex = resourceId.IndexOf('?'); if (queryIndex > -1) { - resourceId = resourceId.Substring(0, queryIndex); + resourceId = resourceId[..queryIndex]; } resourceId = Uri.UnescapeDataString(resourceId); return resourceId; } - private bool ValidateAuthorizationHeader(StringValues authorizationHeader) - { + private bool ValidateAuthorizationHeader(StringValues authorizationHeader) => //TODO: implement header validation http://wopi.readthedocs.io/projects/wopirest/en/latest/bootstrapper/GetRootContainer.html#sample-response // http://stackoverflow.com/questions/31948426/oauth-bearer-token-authentication-is-not-passing-signature-validation - return true; - } + true; } diff --git a/src/WopiHost.Core/Controllers/WopiControllerBase.cs b/src/WopiHost.Core/Controllers/WopiControllerBase.cs index 0ec8715..9a0f703 100644 --- a/src/WopiHost.Core/Controllers/WopiControllerBase.cs +++ b/src/WopiHost.Core/Controllers/WopiControllerBase.cs @@ -10,22 +10,28 @@ namespace WopiHost.Core.Controllers; /// /// Extends the with some basic WOPI-related functionality. /// -public abstract class WopiControllerBase : ControllerBase +/// +/// Default constructor. +/// +/// Object facilitating access to the storage of WOPI files. +/// Object facilitating security-related actions. +/// WOPI Host configuration object +public abstract class WopiControllerBase(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) : ControllerBase { /// /// Provides access to the storage. /// - protected IWopiStorageProvider StorageProvider { get; } + protected IWopiStorageProvider StorageProvider { get; } = storageProvider; /// /// Provides security-related actions. /// - protected IWopiSecurityHandler SecurityHandler { get; } + protected IWopiSecurityHandler SecurityHandler { get; } = securityHandler; /// /// WOPI Host configuration object. /// - protected IOptionsSnapshot WopiHostOptions { get; } + protected IOptionsSnapshot WopiHostOptions { get; } = wopiHostOptions; /// /// WOPI Host base URL @@ -45,19 +51,6 @@ protected string AccessToken } } - /// - /// Default constructor. - /// - /// Object facilitating access to the storage of WOPI files. - /// Object facilitating security-related actions. - /// WOPI Host configuration object - protected WopiControllerBase(IWopiStorageProvider storageProvider, IWopiSecurityHandler securityHandler, IOptionsSnapshot wopiHostOptions) - { - StorageProvider = storageProvider; - SecurityHandler = securityHandler; - WopiHostOptions = wopiHostOptions; - } - /// /// Creates a simple URL to access a WOPI object of choice. /// diff --git a/src/WopiHost.Core/FileExtensions.cs b/src/WopiHost.Core/FileExtensions.cs index 23a90ba..bf5892d 100644 --- a/src/WopiHost.Core/FileExtensions.cs +++ b/src/WopiHost.Core/FileExtensions.cs @@ -22,15 +22,9 @@ public static class FileExtensions /// CheckFileInfo model public static CheckFileInfo GetCheckFileInfo(this IWopiFile file, ClaimsPrincipal principal, HostCapabilities capabilities) { - if (file is null) - { - throw new ArgumentNullException(nameof(file)); - } + ArgumentNullException.ThrowIfNull(file); - if (capabilities is null) - { - throw new ArgumentNullException(nameof(capabilities)); - } + ArgumentNullException.ThrowIfNull(capabilities); var checkFileInfo = new CheckFileInfo(); if (principal is not null) diff --git a/src/WopiHost.Core/HttpHeaderAttribute.cs b/src/WopiHost.Core/HttpHeaderAttribute.cs index 330e937..a48aa47 100644 --- a/src/WopiHost.Core/HttpHeaderAttribute.cs +++ b/src/WopiHost.Core/HttpHeaderAttribute.cs @@ -5,29 +5,20 @@ namespace WopiHost.Core; /// /// A header-based constraint for HTTP actions. /// +/// +/// Creates an instance of a constraint based on a header name and allowed values. +/// +/// Header name to check. +/// Accepted header values. [AttributeUsage(AttributeTargets.Method)] -public class HttpHeaderAttribute : Attribute, IActionConstraint +public class HttpHeaderAttribute(string header, params string[] values) : Attribute, IActionConstraint { - private string Header { get; set; } + private string Header { get; set; } = header; - private string[] Values { get; set; } - - /// - /// Creates an instance of a constraint based on a header name and allowed values. - /// - /// Header name to check. - /// Accepted header values. - public HttpHeaderAttribute(string header, params string[] values) - { - Header = header; - Values = values; - } + private string[] Values { get; set; } = values; /// - public bool Accept(ActionConstraintContext context) - { - return (context is not null) && context.RouteContext.HttpContext.Request.Headers.TryGetValue(Header, out var value) && Values.Contains(value[0]); - } + public bool Accept(ActionConstraintContext context) => (context is not null) && context.RouteContext.HttpContext.Request.Headers.TryGetValue(Header, out var value) && Values.Contains(value[0]); /// public int Order => 0; diff --git a/src/WopiHost.Core/Security/Authentication/AccessTokenHandler.cs b/src/WopiHost.Core/Security/Authentication/AccessTokenHandler.cs index 4447dd9..6393a84 100644 --- a/src/WopiHost.Core/Security/Authentication/AccessTokenHandler.cs +++ b/src/WopiHost.Core/Security/Authentication/AccessTokenHandler.cs @@ -9,7 +9,13 @@ namespace WopiHost.Core.Security.Authentication; /// /// Class facilitating authentication using an access token query parameter. /// -public class AccessTokenHandler : AuthenticationHandler +/// +/// Creates an instance of . +/// +/// The monitor for the options instance. +/// The Microsoft.Extensions.Logging.ILoggerFactory. +/// The System.Text.Encodings.Web.UrlEncoder. +public class AccessTokenHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler(options, logger, encoder) { /// /// Handles authentication using the access_token query parameter. @@ -40,10 +46,10 @@ protected override Task HandleAuthenticateAsync() if (Options.SaveToken) { - ticket.Properties.StoreTokens(new[] - { + ticket.Properties.StoreTokens( + [ new AuthenticationToken { Name = AccessTokenDefaults.ACCESS_TOKEN_QUERY_NAME, Value = token } - }); + ]); } return Task.FromResult(AuthenticateResult.Success(ticket)); } @@ -67,16 +73,4 @@ protected override Task HandleAuthenticateAsync() return Task.FromResult(AuthenticateResult.Fail(ex)); } } - - /// - /// Creates an instance of . - /// - /// The monitor for the options instance. - /// The Microsoft.Extensions.Logging.ILoggerFactory. - /// The System.Text.Encodings.Web.UrlEncoder. - /// The Microsoft.AspNetCore.Authentication.ISystemClock. - public AccessTokenHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) - { - // Used by for Dependency Injection - } } \ No newline at end of file diff --git a/src/WopiHost.Core/Security/Authentication/AuthenticationBuilderExtensions.cs b/src/WopiHost.Core/Security/Authentication/AuthenticationBuilderExtensions.cs index 6d8efd5..ec0f177 100644 --- a/src/WopiHost.Core/Security/Authentication/AuthenticationBuilderExtensions.cs +++ b/src/WopiHost.Core/Security/Authentication/AuthenticationBuilderExtensions.cs @@ -7,16 +7,13 @@ namespace WopiHost.Core.Security.Authentication; /// public static class AuthenticationBuilderExtensions { - /// - /// Adds to the . - /// - /// An instance of - /// Schema name - /// Schema display name - /// A delegate for configuring - /// - public static AuthenticationBuilder AddTokenAuthentication(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) - { - return builder.AddScheme(authenticationScheme, displayName, configureOptions); - } + /// + /// Adds to the . + /// + /// An instance of + /// Schema name + /// Schema display name + /// A delegate for configuring + /// + public static AuthenticationBuilder AddTokenAuthentication(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) => builder.AddScheme(authenticationScheme, displayName, configureOptions); } diff --git a/src/WopiHost.Core/Security/Authorization/WopiAuthorizationHandler.cs b/src/WopiHost.Core/Security/Authorization/WopiAuthorizationHandler.cs index 70b91bd..f7005f5 100644 --- a/src/WopiHost.Core/Security/Authorization/WopiAuthorizationHandler.cs +++ b/src/WopiHost.Core/Security/Authorization/WopiAuthorizationHandler.cs @@ -6,21 +6,16 @@ namespace WopiHost.Core.Security.Authorization; /// /// Performs resource-based authorization. /// -public class WopiAuthorizationHandler : AuthorizationHandler +/// +/// Creates an instance of . +/// +/// AuthNZ handler. +public class WopiAuthorizationHandler(IWopiSecurityHandler securityHandler) : AuthorizationHandler { /// /// Provides authentication and performs authorization operations for WOPI objects /// - public IWopiSecurityHandler SecurityHandler { get; } - - /// - /// Creates an instance of . - /// - /// AuthNZ handler. - public WopiAuthorizationHandler(IWopiSecurityHandler securityHandler) - { - SecurityHandler = securityHandler; - } + public IWopiSecurityHandler SecurityHandler { get; } = securityHandler; /// /// Performs resource-based authorization check. diff --git a/src/WopiHost.Core/Security/FileResource.cs b/src/WopiHost.Core/Security/FileResource.cs index 3800be7..1496903 100644 --- a/src/WopiHost.Core/Security/FileResource.cs +++ b/src/WopiHost.Core/Security/FileResource.cs @@ -1,21 +1,16 @@ namespace WopiHost.Core.Security; - /// - /// Represents a resource for a resource-based authroization. - /// - public class FileResource +/// +/// Represents a resource for a resource-based authroization. +/// +/// +/// Creates an object representing a resource for a resource-based authroization. +/// +/// Identifier of a resource. +public class FileResource(string fileId) { - /// - /// Identifier of a resource. - /// - public string FileId { get; } - - /// - /// Creates an object representing a resource for a resource-based authroization. - /// - /// Identifier of a resource. - public FileResource(string fileId) - { - FileId = fileId; - } + /// + /// Identifier of a resource. + /// + public string FileId { get; } = fileId; } diff --git a/src/WopiHost.Core/WopiHost.Core.csproj b/src/WopiHost.Core/WopiHost.Core.csproj index 00b6dfe..3ba0b0c 100644 --- a/src/WopiHost.Core/WopiHost.Core.csproj +++ b/src/WopiHost.Core/WopiHost.Core.csproj @@ -3,7 +3,7 @@ WopiHost.Core Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.Core WopiHost.Core LICENSE.txt @@ -30,7 +30,7 @@ - + @@ -42,7 +42,7 @@ - - + + diff --git a/src/WopiHost.Core/WopiOverrideHeaderAttribute.cs b/src/WopiHost.Core/WopiOverrideHeaderAttribute.cs index 72e9789..c67dc1b 100644 --- a/src/WopiHost.Core/WopiOverrideHeaderAttribute.cs +++ b/src/WopiHost.Core/WopiOverrideHeaderAttribute.cs @@ -3,14 +3,11 @@ /// /// An action constraint based on the X-WOPI-Override header. /// +/// +/// Creates an instance of the header-based constraint based on allowed values. +/// +/// Accepted header values. [AttributeUsage(AttributeTargets.Method)] -public class WopiOverrideHeaderAttribute : HttpHeaderAttribute +public class WopiOverrideHeaderAttribute(string[] values) : HttpHeaderAttribute(WopiHeaders.WOPI_OVERRIDE, values) { - /// - /// Creates an instance of the header-based constraint based on allowed values. - /// - /// Accepted header values. - public WopiOverrideHeaderAttribute(string[] values) : base(WopiHeaders.WOPI_OVERRIDE, values) - { - } } diff --git a/src/WopiHost.Discovery/AsyncExpiringLazy{T}.cs b/src/WopiHost.Discovery/AsyncExpiringLazy{T}.cs index 4404d12..658f260 100644 --- a/src/WopiHost.Discovery/AsyncExpiringLazy{T}.cs +++ b/src/WopiHost.Discovery/AsyncExpiringLazy{T}.cs @@ -6,23 +6,18 @@ /// https://www.strathweb.com/2016/11/lazy-async-initialization-for-expiring-objects/ /// /// Type of the temporary value. -public class AsyncExpiringLazy +/// +/// Creates a new instance of . +/// +/// A delegate that facilitates the creation of the value. +/// The must be initialized. +public class AsyncExpiringLazy(Func, Task>> valueProvider) { private static readonly SemaphoreSlim SyncLock = new(initialCount: 1); - private readonly Func, Task>> _valueProvider; + private readonly Func, Task>> _valueProvider = valueProvider ?? throw new ArgumentNullException(nameof(valueProvider)); private TemporaryValue _value; private bool IsValueCreatedInternal => _value.Result != null && _value.ValidUntil > DateTimeOffset.UtcNow; - /// - /// Creates a new instance of . - /// - /// A delegate that facilitates the creation of the value. - /// The must be initialized. - public AsyncExpiringLazy(Func, Task>> valueProvider) - { - _valueProvider = valueProvider ?? throw new ArgumentNullException(nameof(valueProvider)); - } - /// /// Returns true if a value has been created and is still valid. /// diff --git a/src/WopiHost.Discovery/FileSystemDiscoveryFileProvider.cs b/src/WopiHost.Discovery/FileSystemDiscoveryFileProvider.cs index c18d59d..7b1d01c 100644 --- a/src/WopiHost.Discovery/FileSystemDiscoveryFileProvider.cs +++ b/src/WopiHost.Discovery/FileSystemDiscoveryFileProvider.cs @@ -5,22 +5,14 @@ namespace WopiHost.Discovery; /// /// Loads the WOPI discovery XML file from the local file system. /// -public class FileSystemDiscoveryFileProvider : IDiscoveryFileProvider +/// +/// Initializes the provider using a local file-system path. +/// +/// Path to a WOPI XML discovery file. +public class FileSystemDiscoveryFileProvider(string filePath) : IDiscoveryFileProvider { - private readonly string _filePath; + private readonly string _filePath = filePath; - /// - /// Initializes the provider using a local file-system path. - /// - /// Path to a WOPI XML discovery file. - public FileSystemDiscoveryFileProvider(string filePath) - { - _filePath = filePath; - } - - /// - public Task GetDiscoveryXmlAsync() - { - return Task.FromResult(XElement.Parse(File.ReadAllText(_filePath))); - } - } \ No newline at end of file + /// + public Task GetDiscoveryXmlAsync() => Task.FromResult(XElement.Parse(File.ReadAllText(_filePath))); +} \ No newline at end of file diff --git a/src/WopiHost.Discovery/HttpDiscoveryFileProvider.cs b/src/WopiHost.Discovery/HttpDiscoveryFileProvider.cs index ec77f38..2a3168a 100644 --- a/src/WopiHost.Discovery/HttpDiscoveryFileProvider.cs +++ b/src/WopiHost.Discovery/HttpDiscoveryFileProvider.cs @@ -5,18 +5,13 @@ namespace WopiHost.Discovery; /// /// A discovery file provider that loads the discovery file from a WOPI client over HTTP. /// -public class HttpDiscoveryFileProvider : IDiscoveryFileProvider +/// +/// Creates an instance of a discovery file provider that loads the discovery file from a WOPI client over HTTP. +/// +/// An HTTP client with a configured to point to a WOPI client. +public class HttpDiscoveryFileProvider(HttpClient httpClient) : IDiscoveryFileProvider { - private readonly HttpClient _httpClient; - - /// - /// Creates an instance of a discovery file provider that loads the discovery file from a WOPI client over HTTP. - /// - /// An HTTP client with a configured to point to a WOPI client. - public HttpDiscoveryFileProvider(HttpClient httpClient) - { - _httpClient = httpClient; - } + private readonly HttpClient _httpClient = httpClient; /// public async Task GetDiscoveryXmlAsync() diff --git a/src/WopiHost.Discovery/WopiDiscoverer.cs b/src/WopiHost.Discovery/WopiDiscoverer.cs index f058a57..2037ebd 100644 --- a/src/WopiHost.Discovery/WopiDiscoverer.cs +++ b/src/WopiHost.Discovery/WopiDiscoverer.cs @@ -4,7 +4,12 @@ namespace WopiHost.Discovery; /// -public class WopiDiscoverer : IDiscoverer +/// +/// Creates a new instance of the , a class for examining the capabilities of the WOPI client. +/// +/// A service that provides the discovery file to examine. +/// +public class WopiDiscoverer(IDiscoveryFileProvider discoveryFileProvider, DiscoveryOptions discoveryOptions) : IDiscoverer { private const string ElementNetZone = "net-zone"; private const string ElementApp = "app"; @@ -20,9 +25,9 @@ public class WopiDiscoverer : IDiscoverer private AsyncExpiringLazy> _apps; - private IDiscoveryFileProvider DiscoveryFileProvider { get; } + private IDiscoveryFileProvider DiscoveryFileProvider { get; } = discoveryFileProvider; - private DiscoveryOptions DiscoveryOptions { get; } + private DiscoveryOptions DiscoveryOptions { get; } = discoveryOptions; private AsyncExpiringLazy> Apps { @@ -43,21 +48,7 @@ private AsyncExpiringLazy> Apps } } - /// - /// Creates a new instance of the , a class for examining the capabilities of the WOPI client. - /// - /// A service that provides the discovery file to examine. - /// - public WopiDiscoverer(IDiscoveryFileProvider discoveryFileProvider, DiscoveryOptions discoveryOptions) - { - DiscoveryFileProvider = discoveryFileProvider; - DiscoveryOptions = discoveryOptions; - } - - internal async Task> GetAppsAsync() - { - return await Apps.Value(); - } + internal async Task> GetAppsAsync() => await Apps.Value(); private bool ValidateNetZone(XElement e) { @@ -80,7 +71,7 @@ public async Task SupportsActionAsync(string extension, WopiActionEnum act { var actionString = action.ToString().ToUpperInvariant(); - var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.ToUpperInvariant() == actionString); + var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.Equals(actionString, StringComparison.InvariantCultureIgnoreCase)); return query.Any(); } @@ -90,7 +81,7 @@ public async Task> GetActionRequirementsAsync(string extensi { var actionString = action.ToString().ToUpperInvariant(); - var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.ToUpperInvariant() == actionString).Select(e => e.Attribute(AttrActionRequires).Value.Split(',')); + var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.Equals(actionString, StringComparison.InvariantCultureIgnoreCase)).Select(e => e.Attribute(AttrActionRequires).Value.Split(',')); return query.FirstOrDefault(); } @@ -106,7 +97,7 @@ public async Task RequiresCobaltAsync(string extension, WopiActionEnum act public async Task GetUrlTemplateAsync(string extension, WopiActionEnum action) { var actionString = action.ToString().ToUpperInvariant(); - var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.ToUpperInvariant() == actionString).Select(e => e.Attribute(AttrActionUrl).Value); + var query = (await GetAppsAsync()).Elements().Where(e => (string)e.Attribute(AttrActionExtension) == extension && e.Attribute(AttrActionName).Value.Equals(actionString, StringComparison.InvariantCultureIgnoreCase)).Select(e => e.Attribute(AttrActionUrl).Value); return query.FirstOrDefault(); } diff --git a/src/WopiHost.Discovery/WopiHost.Discovery.csproj b/src/WopiHost.Discovery/WopiHost.Discovery.csproj index 90f68d7..26ac96e 100644 --- a/src/WopiHost.Discovery/WopiHost.Discovery.csproj +++ b/src/WopiHost.Discovery/WopiHost.Discovery.csproj @@ -3,7 +3,7 @@ WopiHost.Discovery Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.Discovery WopiHost.Discovery LICENSE.txt @@ -33,7 +33,7 @@ - + diff --git a/src/WopiHost.FileSystemProvider/WopiFile.cs b/src/WopiHost.FileSystemProvider/WopiFile.cs index f829558..4f8a9b5 100644 --- a/src/WopiHost.FileSystemProvider/WopiFile.cs +++ b/src/WopiHost.FileSystemProvider/WopiFile.cs @@ -7,20 +7,25 @@ namespace WopiHost.FileSystemProvider; /// -public class WopiFile : IWopiFile +/// +/// Creates an instance of . +/// +/// Path on the file system the file is located in. +/// Identifier of a file. +public class WopiFile(string filePath, string fileIdentifier) : IWopiFile { private FileInfo _fileInfo; private FileVersionInfo _fileVersionInfo; - private string FilePath { get; set; } + private string FilePath { get; set; } = filePath; private FileInfo FileInfo => _fileInfo ??= new FileInfo(FilePath); private FileVersionInfo FileVersionInfo => _fileVersionInfo ??= FileVersionInfo.GetVersionInfo(FilePath); /// - public string Identifier { get; } + public string Identifier { get; } = fileIdentifier; /// public bool Exists => FileInfo.Exists; @@ -31,7 +36,7 @@ public string Extension get { var ext = FileInfo.Extension; - if (ext.StartsWith(".", StringComparison.InvariantCulture)) + if (ext.StartsWith('.')) { ext = ext[1..]; } @@ -54,28 +59,11 @@ public string Extension /// public DateTime LastWriteTimeUtc => FileInfo.LastWriteTimeUtc; - /// - /// Creates an instance of . - /// - /// Path on the file system the file is located in. - /// Identifier of a file. - public WopiFile(string filePath, string fileIdentifier) - { - FilePath = filePath; - Identifier = fileIdentifier; - } - /// - public Stream GetReadStream() - { - return FileInfo.OpenRead(); - } + public Stream GetReadStream() => FileInfo.OpenRead(); /// - public Stream GetWriteStream() - { - return FileInfo.Open(FileMode.Truncate); - } + public Stream GetWriteStream() => FileInfo.Open(FileMode.Truncate); /// /// A string that uniquely identifies the owner of the file. diff --git a/src/WopiHost.FileSystemProvider/WopiFileSystemProvider.cs b/src/WopiHost.FileSystemProvider/WopiFileSystemProvider.cs index 689000a..f561547 100644 --- a/src/WopiHost.FileSystemProvider/WopiFileSystemProvider.cs +++ b/src/WopiHost.FileSystemProvider/WopiFileSystemProvider.cs @@ -35,10 +35,7 @@ public class WopiFileSystemProvider : IWopiStorageProvider /// Application configuration. public WopiFileSystemProvider(IHostEnvironment env, IConfiguration configuration) { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } + ArgumentNullException.ThrowIfNull(configuration); HostEnvironment = env ?? throw new ArgumentNullException(nameof(env)); FileSystemProviderOptions = configuration.GetSection(WopiConfigurationSections.STORAGE_OPTIONS).Get(); //TODO: rework diff --git a/src/WopiHost.FileSystemProvider/WopiFolder.cs b/src/WopiHost.FileSystemProvider/WopiFolder.cs index 55dcd2c..e1441ff 100644 --- a/src/WopiHost.FileSystemProvider/WopiFolder.cs +++ b/src/WopiHost.FileSystemProvider/WopiFolder.cs @@ -3,30 +3,24 @@ namespace WopiHost.FileSystemProvider; /// -public class WopiFolder : IWopiFolder +/// +/// Creates an instance of . +/// +/// Path on the file system the folder is located in. +/// A unique identifier of a folder. +public class WopiFolder(string path, string folderIdentifier) : IWopiFolder { private DirectoryInfo _folderInfo; - /// - protected string Path { get; set; } + /// + protected string Path { get; set; } = path; - /// - protected DirectoryInfo FolderInfo => _folderInfo ??= new DirectoryInfo(Path); + /// + protected DirectoryInfo FolderInfo => _folderInfo ??= new DirectoryInfo(Path); /// public string Name => FolderInfo.Name; - /// - public string Identifier { get; } - - /// - /// Creates an instance of . - /// - /// Path on the file system the folder is located in. - /// A unique identifier of a folder. - public WopiFolder(string path, string folderIdentifier) - { - Path = path; - Identifier = folderIdentifier; - } - } + /// + public string Identifier { get; } = folderIdentifier; +} diff --git a/src/WopiHost.FileSystemProvider/WopiHost.FileSystemProvider.csproj b/src/WopiHost.FileSystemProvider/WopiHost.FileSystemProvider.csproj index 082406f..7291f74 100644 --- a/src/WopiHost.FileSystemProvider/WopiHost.FileSystemProvider.csproj +++ b/src/WopiHost.FileSystemProvider/WopiHost.FileSystemProvider.csproj @@ -3,7 +3,7 @@ WopiHost.FileSystemProvider Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.FileSystemProvider WopiHost.FileSystemProvider LICENSE.txt @@ -33,26 +33,22 @@ - - + + + + + + + + + + + - - - - - - - - - - - - - - + diff --git a/src/WopiHost.FileSystemProvider/WopiSecurityHandler.cs b/src/WopiHost.FileSystemProvider/WopiSecurityHandler.cs index 69760b1..99698bd 100644 --- a/src/WopiHost.FileSystemProvider/WopiSecurityHandler.cs +++ b/src/WopiHost.FileSystemProvider/WopiSecurityHandler.cs @@ -8,9 +8,13 @@ namespace WopiHost.FileSystemProvider; /// -public class WopiSecurityHandler : IWopiSecurityHandler +/// +/// Creates a new instance of the . +/// +/// An instance of a type used to configure the logging system and create instances of Microsoft.Extensions.Logging.ILogger from the registered Microsoft.Extensions.Logging.ILoggerProviders. +public class WopiSecurityHandler(ILoggerFactory loggerFactory) : IWopiSecurityHandler { - private readonly ILogger _logger; + private readonly ILogger _logger = loggerFactory.CreateLogger(); private readonly JwtSecurityTokenHandler _tokenHandler = new(); private SymmetricSecurityKey _key = null; @@ -50,15 +54,6 @@ private SymmetricSecurityKey Key } }; - /// - /// Creates a new instance of the . - /// - /// An instance of a type used to configure the logging system and create instances of Microsoft.Extensions.Logging.ILogger from the registered Microsoft.Extensions.Logging.ILoggerProviders. - public WopiSecurityHandler(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - } - /// public SecurityToken GenerateAccessToken(string userId, string resourceId) { @@ -102,17 +97,12 @@ public ClaimsPrincipal GetPrincipal(string tokenString) } /// - public bool IsAuthorized(ClaimsPrincipal principal, string resourceId, WopiAuthorizationRequirement operation) - { + public bool IsAuthorized(ClaimsPrincipal principal, string resourceId, WopiAuthorizationRequirement operation) => //TODO: logic - return true; - } + true; /// /// Converts the security token to a Base64 string. /// - public string WriteToken(SecurityToken token) - { - return _tokenHandler.WriteToken(token); - } + public string WriteToken(SecurityToken token) => _tokenHandler.WriteToken(token); } diff --git a/src/WopiHost.Url/WopiHost.Url.csproj b/src/WopiHost.Url/WopiHost.Url.csproj index a36f45f..1264e49 100644 --- a/src/WopiHost.Url/WopiHost.Url.csproj +++ b/src/WopiHost.Url/WopiHost.Url.csproj @@ -3,7 +3,7 @@ WopiHost.Url Class Library Petr Svihlik - net7.0 + net8.0 WopiHost.Url WopiHost.Url LICENSE.txt @@ -33,7 +33,7 @@ - + diff --git a/src/WopiHost.Url/WopiUrlBuilder.cs b/src/WopiHost.Url/WopiUrlBuilder.cs index 236c0c6..d0d4143 100644 --- a/src/WopiHost.Url/WopiUrlBuilder.cs +++ b/src/WopiHost.Url/WopiUrlBuilder.cs @@ -10,28 +10,22 @@ namespace WopiHost.Url; /// WOPI v2 spec: http://wopi.readthedocs.io/en/latest/discovery.html /// WOPI v1 spec: https://msdn.microsoft.com/en-us/library/hh695362(v=office.12).aspx /// -public partial class WopiUrlBuilder +/// +/// Creates a new instance of WOPI URL generator class. +/// +/// Provider of WOPI discovery data. +/// Additional settings influencing behavior of the WOPI client. +public partial class WopiUrlBuilder(IDiscoverer discoverer, WopiUrlSettings urlSettings = null) { [GeneratedRegex("<(?\\w*)=(?\\w*)&*>")] private static partial Regex UrlParamRegex(); - private readonly IDiscoverer _wopiDiscoverer; + private readonly IDiscoverer _wopiDiscoverer = discoverer; /// /// Additional URL parameters influencing the behavior of the WOPI client. /// - public WopiUrlSettings UrlSettings { get; } - - /// - /// Creates a new instance of WOPI URL generator class. - /// - /// Provider of WOPI discovery data. - /// Additional settings influencing behavior of the WOPI client. - public WopiUrlBuilder(IDiscoverer discoverer, WopiUrlSettings urlSettings = null) - { - _wopiDiscoverer = discoverer; - UrlSettings = urlSettings; - } + public WopiUrlSettings UrlSettings { get; } = urlSettings; /// /// Generates an URL for a given file and action. diff --git a/src/WopiHost.Web/Controllers/HomeController.cs b/src/WopiHost.Web/Controllers/HomeController.cs index d441daa..ab0ec79 100644 --- a/src/WopiHost.Web/Controllers/HomeController.cs +++ b/src/WopiHost.Web/Controllers/HomeController.cs @@ -10,27 +10,19 @@ namespace WopiHost.Web.Controllers; -public class HomeController : Controller +public class HomeController(IOptionsSnapshot wopiOptions, IWopiStorageProvider storageProvider, IDiscoverer discoverer, ILoggerFactory loggerFactory) : Controller { private WopiUrlBuilder _urlGenerator; - private IOptionsSnapshot WopiOptions { get; } - private IWopiStorageProvider StorageProvider { get; } - private IDiscoverer Discoverer { get; } - private ILoggerFactory LoggerFactory { get; } + private IOptionsSnapshot WopiOptions { get; } = wopiOptions; + private IWopiStorageProvider StorageProvider { get; } = storageProvider; + private IDiscoverer Discoverer { get; } = discoverer; + private ILoggerFactory LoggerFactory { get; } = loggerFactory; //TODO: remove test culture value and load it from configuration SECTION public WopiUrlBuilder UrlGenerator => _urlGenerator ??= new WopiUrlBuilder(Discoverer, new WopiUrlSettings { UiLlcc = new CultureInfo("en-US") }); - public HomeController(IOptionsSnapshot wopiOptions, IWopiStorageProvider storageProvider, IDiscoverer discoverer, ILoggerFactory loggerFactory) - { - WopiOptions = wopiOptions; - StorageProvider = storageProvider; - Discoverer = discoverer; - LoggerFactory = loggerFactory; - } - public async Task Index() { try diff --git a/src/WopiHost.Web/Program.cs b/src/WopiHost.Web/Program.cs index 9363623..af5981a 100644 --- a/src/WopiHost.Web/Program.cs +++ b/src/WopiHost.Web/Program.cs @@ -2,10 +2,7 @@ public class Program { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } + public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) diff --git a/src/WopiHost.Web/ServiceCollectionExtensions.cs b/src/WopiHost.Web/ServiceCollectionExtensions.cs index 54ebfef..48779b0 100644 --- a/src/WopiHost.Web/ServiceCollectionExtensions.cs +++ b/src/WopiHost.Web/ServiceCollectionExtensions.cs @@ -4,8 +4,8 @@ public static class ServiceCollectionExtensions { public static TConfig Configure(this IServiceCollection services, IConfiguration configuration) where TConfig : class, new() { - if (services == null) throw new ArgumentNullException(nameof(services)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configuration); var config = new TConfig(); configuration.Bind(config); @@ -15,9 +15,9 @@ public static class ServiceCollectionExtensions public static TConfig Configure(this IServiceCollection services, IConfiguration configuration, Func pocoProvider) where TConfig : class { - if (services == null) throw new ArgumentNullException(nameof(services)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - if (pocoProvider == null) throw new ArgumentNullException(nameof(pocoProvider)); + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(pocoProvider); var config = pocoProvider(); configuration.Bind(config); @@ -27,9 +27,9 @@ public static TConfig Configure(this IServiceCollection services, IConf public static TConfig Configure(this IServiceCollection services, IConfiguration configuration, TConfig config) where TConfig : class { - if (services == null) throw new ArgumentNullException(nameof(services)); - if (configuration == null) throw new ArgumentNullException(nameof(configuration)); - if (config == null) throw new ArgumentNullException(nameof(config)); + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(config); configuration.Bind(config); services.AddSingleton(config); diff --git a/src/WopiHost.Web/Startup.cs b/src/WopiHost.Web/Startup.cs index b0dcbf0..d3195ac 100644 --- a/src/WopiHost.Web/Startup.cs +++ b/src/WopiHost.Web/Startup.cs @@ -5,14 +5,9 @@ namespace WopiHost.Web; -public class Startup +public class Startup(IConfiguration configuration) { - public IConfiguration Configuration { get; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public IConfiguration Configuration { get; } = configuration; /// /// Sets up the DI container. diff --git a/src/WopiHost.Web/Views/Home/Index.cshtml b/src/WopiHost.Web/Views/Home/Index.cshtml index 0a84a11..8d25cd0 100644 --- a/src/WopiHost.Web/Views/Home/Index.cshtml +++ b/src/WopiHost.Web/Views/Home/Index.cshtml @@ -1,6 +1,6 @@ @using WopiHost.Discovery.Enumerations -@model IEnumerable +@model IEnumerable @{ ViewData["Title"] = "List of WOPI files"; } diff --git a/src/WopiHost.Web/WopiHost.Web.csproj b/src/WopiHost.Web/WopiHost.Web.csproj index 045eb67..7df9991 100644 --- a/src/WopiHost.Web/WopiHost.Web.csproj +++ b/src/WopiHost.Web/WopiHost.Web.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true true WopiHost.Web @@ -13,7 +13,7 @@ - + diff --git a/src/WopiHost/LogHelper.cs b/src/WopiHost/LogHelper.cs index bcc841a..16103c2 100644 --- a/src/WopiHost/LogHelper.cs +++ b/src/WopiHost/LogHelper.cs @@ -15,15 +15,9 @@ public static class LogHelper /// HTTP context instance public static void EnrichWithWopiDiagnostics(IDiagnosticContext diagnosticContext, HttpContext httpContext) { - if (diagnosticContext is null) - { - throw new System.ArgumentNullException(nameof(diagnosticContext)); - } + ArgumentNullException.ThrowIfNull(diagnosticContext); - if (httpContext is null) - { - throw new System.ArgumentNullException(nameof(httpContext)); - } + ArgumentNullException.ThrowIfNull(httpContext); var request = httpContext.Request; diff --git a/src/WopiHost/Startup.cs b/src/WopiHost/Startup.cs index bce0130..54425f6 100644 --- a/src/WopiHost/Startup.cs +++ b/src/WopiHost/Startup.cs @@ -7,14 +7,9 @@ namespace WopiHost; -public class Startup +public class Startup(IConfiguration configuration) { - public IConfiguration Configuration { get; set; } - - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public IConfiguration Configuration { get; set; } = configuration; public void ConfigureContainer(ContainerBuilder builder) { diff --git a/src/WopiHost/WopiHost.csproj b/src/WopiHost/WopiHost.csproj index 55bdb98..1b033d1 100644 --- a/src/WopiHost/WopiHost.csproj +++ b/src/WopiHost/WopiHost.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 WopiHost Exe true @@ -23,19 +23,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 0aab062..bf02000 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -1,7 +1,6 @@ Petr Svihlik - net7.0 false enable enable diff --git a/test/WopiHost.Core.Tests/WopiHost.Core.Tests.csproj b/test/WopiHost.Core.Tests/WopiHost.Core.Tests.csproj index a0ef820..8e60abb 100644 --- a/test/WopiHost.Core.Tests/WopiHost.Core.Tests.csproj +++ b/test/WopiHost.Core.Tests/WopiHost.Core.Tests.csproj @@ -1,5 +1,29 @@  + + net8.0 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + diff --git a/test/WopiHost.Discovery.Tests/WopiDiscovererTests.cs b/test/WopiHost.Discovery.Tests/WopiDiscovererTests.cs index 66f8476..6a48fc9 100644 --- a/test/WopiHost.Discovery.Tests/WopiDiscovererTests.cs +++ b/test/WopiHost.Discovery.Tests/WopiDiscovererTests.cs @@ -15,10 +15,7 @@ public WopiDiscovererTests() { } - private void InitDiscoverer(string fileName, NetZoneEnum netZone) - { - _wopiDiscoverer = new WopiDiscoverer(new FileSystemDiscoveryFileProvider(Path.Combine(AppContext.BaseDirectory, fileName)), new DiscoveryOptions { NetZone = netZone }); - } + private void InitDiscoverer(string fileName, NetZoneEnum netZone) => _wopiDiscoverer = new WopiDiscoverer(new FileSystemDiscoveryFileProvider(Path.Combine(AppContext.BaseDirectory, fileName)), new DiscoveryOptions { NetZone = netZone }); [Theory] [InlineData(NetZoneEnum.ExternalHttps, "xlsm", WopiActionEnum.LegacyWebService, "https://excel.officeapps.live.com/x/_vti_bin/excelserviceinternal.asmx?", XmlOo2019)] @@ -145,7 +142,7 @@ public async void SupportedExtensionCobalt(NetZoneEnum netZone, string extension [InlineData(NetZoneEnum.InternalHttp, "docx", WopiActionEnum.Edit, "http://owaserver/we/wordeditorframe.aspx?", XmlOwa2013)] [InlineData(NetZoneEnum.InternalHttp, "html", WopiActionEnum.Edit, null, XmlOwa2013)] [InlineData(NetZoneEnum.InternalHttp, "txt", WopiActionEnum.Edit, null, XmlOwa2013)] - public async void UrlTemplateTests(NetZoneEnum netZone, string extension, WopiActionEnum action, string expectedValue, string fileName) + public async void UrlTemplateTests(NetZoneEnum netZone, string extension, WopiActionEnum action, string? expectedValue, string fileName) { // Arrange InitDiscoverer(fileName, netZone); @@ -162,7 +159,7 @@ public async void UrlTemplateTests(NetZoneEnum netZone, string extension, WopiAc [InlineData(NetZoneEnum.InternalHttp, "docx", "Word", XmlOos2016)] [InlineData(NetZoneEnum.InternalHttp, "html", null, XmlOos2016)] [InlineData(NetZoneEnum.InternalHttp, "txt", null, XmlOos2016)] - public async void AppNameTests(NetZoneEnum netZone, string extension, string expectedValue, string fileName) + public async void AppNameTests(NetZoneEnum netZone, string extension, string? expectedValue, string fileName) { // Arrange InitDiscoverer(fileName, netZone); @@ -179,7 +176,7 @@ public async void AppNameTests(NetZoneEnum netZone, string extension, string exp [InlineData(NetZoneEnum.InternalHttp, "docx", "http://owaserver/wv/resources/1033/FavIcon_Word.ico", XmlOos2016)] [InlineData(NetZoneEnum.InternalHttp, "html", null, XmlOos2016)] [InlineData(NetZoneEnum.InternalHttp, "txt", null, XmlOos2016)] - public async void FavIconTests(NetZoneEnum netZone, string extension, string expectedValue, string fileName) + public async void FavIconTests(NetZoneEnum netZone, string extension, string? expectedValue, string fileName) { // Arrange InitDiscoverer(fileName, netZone); diff --git a/test/WopiHost.Discovery.Tests/WopiHost.Discovery.Tests.csproj b/test/WopiHost.Discovery.Tests/WopiHost.Discovery.Tests.csproj index ee521b4..9fcfa99 100644 --- a/test/WopiHost.Discovery.Tests/WopiHost.Discovery.Tests.csproj +++ b/test/WopiHost.Discovery.Tests/WopiHost.Discovery.Tests.csproj @@ -5,7 +5,33 @@ + + net8.0 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + diff --git a/test/WopiHost.FileSystemProvider.Tests/WopiHost.FileSystemProvider.Tests.csproj b/test/WopiHost.FileSystemProvider.Tests/WopiHost.FileSystemProvider.Tests.csproj index cedd729..6efa1c4 100644 --- a/test/WopiHost.FileSystemProvider.Tests/WopiHost.FileSystemProvider.Tests.csproj +++ b/test/WopiHost.FileSystemProvider.Tests/WopiHost.FileSystemProvider.Tests.csproj @@ -1,5 +1,29 @@  + + net8.0 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + diff --git a/test/WopiHost.FileSystemProvider.Tests/WopiSecurityHandlerTests.cs b/test/WopiHost.FileSystemProvider.Tests/WopiSecurityHandlerTests.cs index 9c51d97..7c54daf 100644 --- a/test/WopiHost.FileSystemProvider.Tests/WopiSecurityHandlerTests.cs +++ b/test/WopiHost.FileSystemProvider.Tests/WopiSecurityHandlerTests.cs @@ -3,8 +3,5 @@ public class WopiSecurityHandlerTests { [Fact] - public void DummyTest() - { - Assert.True(true); - } + public void DummyTest() => Assert.True(true); } diff --git a/test/WopiHost.Url.Tests/WopiHost.Url.Tests.csproj b/test/WopiHost.Url.Tests/WopiHost.Url.Tests.csproj index 2d0d8a2..0c1a098 100644 --- a/test/WopiHost.Url.Tests/WopiHost.Url.Tests.csproj +++ b/test/WopiHost.Url.Tests/WopiHost.Url.Tests.csproj @@ -1,5 +1,29 @@  + + net8.0 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + diff --git a/test/WopiHost.Url.Tests/WopiUrlGeneratorTests.cs b/test/WopiHost.Url.Tests/WopiUrlGeneratorTests.cs index 66401d2..651653d 100644 --- a/test/WopiHost.Url.Tests/WopiUrlGeneratorTests.cs +++ b/test/WopiHost.Url.Tests/WopiUrlGeneratorTests.cs @@ -49,7 +49,7 @@ public async void UrlWithAdditionalSettings(string extension, string wopiFileUrl [Theory] [InlineData("html", "http://wopihost:5000/wopi/files/test.xlsx", WopiActionEnum.Edit, null)] - public async void NonExistentTemplate(string extension, string wopiFileUrl, WopiActionEnum action, string expectedValue) + public async void NonExistentTemplate(string extension, string wopiFileUrl, WopiActionEnum action, string? expectedValue) { // Arrange var urlGenerator = new WopiUrlBuilder(_discoverer);