Skip to content

Enable nullable context in Crypter.API #659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions Crypter.API/Attributes/MaybeAuthorizeAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,17 @@
using EasyMonads;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;

namespace Crypter.API.Attributes;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
internal class MaybeAuthorizeAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
string authorization = context.HttpContext.Request.Headers.Authorization;
bool authorizationProvided = !string.IsNullOrEmpty(authorization);

if (authorizationProvided)
string? authorization = context.HttpContext.Request.Headers.Authorization;
if (!string.IsNullOrEmpty(authorization))
{
string[] authorizationParts = authorization.Split(' ');
if (authorizationParts.Length != 2 || authorizationParts[0].ToLower() != "bearer")
Expand All @@ -51,7 +50,7 @@ public void OnAuthorization(AuthorizationFilterContext context)
}

ITokenService tokenService =
(ITokenService)context.HttpContext.RequestServices.GetService(typeof(ITokenService));
(ITokenService)context.HttpContext.RequestServices.GetRequiredService(typeof(ITokenService));
Maybe<ClaimsPrincipal> maybeClaims = tokenService.ValidateToken(authorizationParts[1]);

maybeClaims.IfSome(x => context.HttpContext.User = x);
Expand Down
10 changes: 5 additions & 5 deletions Crypter.API/Configuration/SwaggerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static class SwaggerConfiguration
{
internal static void AddSwaggerGenOptions(this SwaggerGenOptions options)
{
OpenApiSecurityScheme securityDefinition = new()
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme
{
Name = "Bearer",
BearerFormat = "JWT",
Expand All @@ -45,18 +45,18 @@ internal static void AddSwaggerGenOptions(this SwaggerGenOptions options)
Type = SecuritySchemeType.Http
};

OpenApiSecurityScheme securityScheme = new()
OpenApiSecurityScheme securityScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference()
Reference = new OpenApiReference
{
Id = "jwt_auth",
Type = ReferenceType.SecurityScheme
}
};

OpenApiSecurityRequirement securityRequirements = new()
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement
{
{ securityScheme, Array.Empty<string>() },
{ securityScheme, Array.Empty<string>() }
};

options.AddSecurityDefinition("jwt_auth", securityDefinition);
Expand Down
8 changes: 4 additions & 4 deletions Crypter.API/Contracts/ModelBinders/FormDataJsonBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public Task BindModelAsync(ModelBindingContext bindingContext)
throw new ArgumentNullException(nameof(bindingContext));
}

var modelName = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
string modelName = bindingContext.ModelName;
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

if (valueProviderResult == ValueProviderResult.None)
{
Expand All @@ -29,15 +29,15 @@ public Task BindModelAsync(ModelBindingContext bindingContext)

bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);

var value = valueProviderResult.FirstValue;
string? value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}

try
{
var result = JsonSerializer.Deserialize(value, bindingContext.ModelType);
object? result = JsonSerializer.Deserialize(value, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
catch (Exception)
Expand Down
4 changes: 2 additions & 2 deletions Crypter.API/Contracts/UploadFileTransferReceipt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace Crypter.API.Contracts;
public class UploadFileTransferReceipt
{
[ModelBinder(BinderType = typeof(FormDataJsonBinder))]
public UploadFileTransferRequest Data { get; set; }
public UploadFileTransferRequest? Data { get; init; }

public IFormFile Ciphertext { get; set; }
public IFormFile? Ciphertext { get; init; }
}
4 changes: 2 additions & 2 deletions Crypter.API/Contracts/UploadMessageTransferReceipt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace Crypter.API.Contracts;
public class UploadMessageTransferReceipt
{
[ModelBinder(BinderType = typeof(FormDataJsonBinder))]
public UploadMessageTransferRequest Data { get; set; }
public UploadMessageTransferRequest? Data { get; init; }

public IFormFile Ciphertext { get; set; }
public IFormFile? Ciphertext { get; init; }
}
4 changes: 2 additions & 2 deletions Crypter.API/Controllers/FileTransferController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ public FileTransferController(ITransferDownloadService transferDownloadService,
[MaybeAuthorize]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UploadTransferResponse))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))]
public async Task<IActionResult> UploadFileTransferAsync([FromQuery] string username,
public async Task<IActionResult> UploadFileTransferAsync([FromQuery] string? username,
[FromForm] UploadFileTransferReceipt request)
{
Maybe<string> maybeUsername = string.IsNullOrEmpty(username)
? Maybe<string>.None
: username;
await using Stream ciphertextStream = request.Ciphertext.OpenReadStream();
await using Stream? ciphertextStream = request.Ciphertext?.OpenReadStream();

return await TransferUploadService
.UploadFileTransferAsync(PossibleUserId, maybeUsername, request.Data, ciphertextStream)
Expand Down
4 changes: 2 additions & 2 deletions Crypter.API/Controllers/MessageTransferController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ public MessageTransferController(ITransferDownloadService transferDownloadServic
[MaybeAuthorize]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(UploadTransferResponse))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))]
public async Task<IActionResult> UploadMessageTransferAsync([FromQuery] string username,
public async Task<IActionResult> UploadMessageTransferAsync([FromQuery] string? username,
[FromForm] UploadMessageTransferReceipt request)
{
Maybe<string> maybeUsername = string.IsNullOrEmpty(username)
? Maybe<string>.None
: username;
await using Stream ciphertextStream = request.Ciphertext.OpenReadStream();
await using Stream? ciphertextStream = request.Ciphertext?.OpenReadStream();

return await TransferUploadService
.UploadMessageTransferAsync(PossibleUserId, maybeUsername, request.Data, ciphertextStream)
Expand Down
2 changes: 1 addition & 1 deletion Crypter.API/Controllers/UserAuthenticationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ or RegistrationError.InvalidEmailAddress
};
#pragma warning restore CS8524
}

return await _userAuthenticationService.RegisterAsync(request)
.MatchAsync(
MakeErrorResponse,
Expand Down
2 changes: 1 addition & 1 deletion Crypter.API/Controllers/UserKeyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ IActionResult MakeErrorResponse(InsertKeyPairError error)
return error switch
{
InsertKeyPairError.UnknownError => MakeErrorResponseBase(HttpStatusCode.InternalServerError, error),
InsertKeyPairError.KeyPairAlreadyExists => MakeErrorResponseBase(HttpStatusCode.Conflict, error),
InsertKeyPairError.KeyPairAlreadyExists => MakeErrorResponseBase(HttpStatusCode.Conflict, error)
};
#pragma warning restore CS8524
}
Expand Down
6 changes: 2 additions & 4 deletions Crypter.API/Controllers/UserRecoveryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,8 @@ public IActionResult SendRecoveryEmail([FromQuery] string emailAddress)
_hangfireBackgroundService.SendRecoveryEmailAsync(validEmailAddress.Value));
return Accepted();
}
else
{
return MakeErrorResponseBase(HttpStatusCode.BadRequest, SendRecoveryEmailError.InvalidEmailAddress);
}

return MakeErrorResponseBase(HttpStatusCode.BadRequest, SendRecoveryEmailError.InvalidEmailAddress);
}

[HttpPost]
Expand Down
2 changes: 1 addition & 1 deletion Crypter.API/Crypter.API.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>disable</Nullable>
<Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Contact the current copyright holder to discuss commercial license options.
*/

using System;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

namespace Crypter.API.MetadataProviders;
Expand Down
8 changes: 5 additions & 3 deletions Crypter.API/Methods/HeadersParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
*/

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

namespace Crypter.API.Methods;

public class HeadersParser
public static class HeadersParser
{
public static string GetUserAgent(IHeaderDictionary headers)
{
headers.TryGetValue("User-Agent", out var someUserAgent);
return someUserAgent.ToString() ?? "Unknown device";
return headers.TryGetValue("User-Agent", out StringValues userAgent)
? userAgent.ToString()
: "Unknown device";
}
}
13 changes: 8 additions & 5 deletions Crypter.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
using System.Linq;
using System.Net;
using Crypter.API.Configuration;
using Crypter.API.MetadataProviders;
using Crypter.API.Middleware;
using Crypter.Common.Contracts;
using Crypter.Core;
using Crypter.Core.Exceptions;
using Crypter.Core.Identity;
using Crypter.Core.Models;
using Crypter.Core.Settings;
Expand All @@ -46,10 +48,10 @@

TokenSettings tokenSettings = builder.Configuration
.GetSection("TokenSettings")
.Get<TokenSettings>();
.Get<TokenSettings>() ?? throw new ConfigurationException("TokenSettings not found");

string hangfireConnectionString = builder.Configuration
.GetConnectionString("HangfireConnection");
.GetConnectionString("HangfireConnection") ?? throw new ConfigurationException("HangfireConnection not found");

builder.Services.AddCrypterCore(
builder.Configuration
Expand Down Expand Up @@ -89,7 +91,8 @@
ErrorResponse errorResponse = new ErrorResponse((int)HttpStatusCode.BadRequest, errors);
return new BadRequestObjectResult(errorResponse);
};
});
})
.AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add(new EmptyStringMetaDataProvider()));

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(SwaggerConfiguration.AddSwaggerGenOptions);
Expand All @@ -115,7 +118,7 @@
{
CorsSettings corsSettings = app.Configuration
.GetSection("CorsSettings")
.Get<CorsSettings>();
.Get<CorsSettings>() ?? throw new ConfigurationException("CorsSettings not found");
app.UseCors(x =>
{
x.AllowAnyMethod();
Expand All @@ -126,7 +129,7 @@

DatabaseSettings dbSettings = app.Configuration
.GetSection("DatabaseSettings")
.Get<DatabaseSettings>();
.Get<DatabaseSettings>() ?? throw new ConfigurationException("DatabaseSettings not found");
await app.MigrateDatabaseAsync(dbSettings);

app.UseHttpsRedirection();
Expand Down
35 changes: 35 additions & 0 deletions Crypter.Core/Exceptions/ConfigurationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Crypter File Transfer
*
* This file is part of the Crypter file transfer project.
*
* Crypter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Crypter source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* You can be released from the requirements of the aforementioned license
* by purchasing a commercial license. Buying such a license is mandatory
* as soon as you develop commercial activities involving the Crypter source
* code without disclosing the source code of your own applications.
*
* Contact the current copyright holder to discuss commercial license options.
*/

using System;

namespace Crypter.Core.Exceptions;

public class ConfigurationException : Exception
{
public ConfigurationException(string message) : base(message)
{ }
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ await loginResult.DoRightAsync(async loginResponse =>
});
});

Assert.That(uploadResult.IsRight);

string uploadId = uploadResult
.Map(x => x.HashId)
.RightOrDefault(null);
Expand Down