Skip to content

Commit

Permalink
Version 1.0.1
Browse files Browse the repository at this point in the history
* Sanitized typeName prior to using it. (#30)

* Added release notes and updated NuGet packages. (#31)
  • Loading branch information
steven-rothwell authored Nov 12, 2023
1 parent 0097c7d commit 5fa11ee
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 33 deletions.
7 changes: 7 additions & 0 deletions Crud.Api/Constants/Default.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Crud.Api.Constants
{
public static class Default
{
public const String TypeName = "DefaultTypeName";
}
}
100 changes: 73 additions & 27 deletions Crud.Api/Controllers/CrudController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ public class CrudController : BaseApiController
private readonly IQueryCollectionService _queryCollectionService;
private readonly IPreprocessingService _preprocessingService;
private readonly IPostprocessingService _postprocessingService;
private readonly ISanitizerService _sanitizerService;

public CrudController(IOptions<ApplicationOptions> applicationOptions, ILogger<CrudController> logger, IValidator validator, IPreserver preserver, IStreamService streamService, ITypeService typeService, IQueryCollectionService queryCollectionService,
IPreprocessingService preprocessingService, IPostprocessingService postprocessingService)
IPreprocessingService preprocessingService, IPostprocessingService postprocessingService, ISanitizerService sanitizerService)
: base(applicationOptions)
{
_logger = logger;
Expand All @@ -41,14 +42,19 @@ public CrudController(IOptions<ApplicationOptions> applicationOptions, ILogger<C
_queryCollectionService = queryCollectionService;
_preprocessingService = preprocessingService;
_postprocessingService = postprocessingService;
_sanitizerService = sanitizerService;
}

[Route("{typeName}"), HttpPost]
public async Task<IActionResult> CreateAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -80,17 +86,21 @@ public async Task<IActionResult> CreateAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error creating with typeName: {typeName}.");
_logger.LogError(ex, $"Error creating with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("{typeName}/{id:guid}"), HttpGet]
public async Task<IActionResult> ReadAsync(String typeName, Guid id)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand All @@ -108,7 +118,7 @@ public async Task<IActionResult> ReadAsync(String typeName, Guid id)
model = await (dynamic)readAsync.Invoke(_preserver, new object[] { id });

if (model is null)
return NotFound(String.Format(ErrorMessage.NotFoundRead, typeName));
return NotFound(String.Format(ErrorMessage.NotFoundRead, sanitizedTypeName));

var postprocessingMessageResult = (MessageResult)await _postprocessingService.PostprocessReadAsync(model, id);
if (!postprocessingMessageResult.IsSuccessful)
Expand All @@ -118,17 +128,21 @@ public async Task<IActionResult> ReadAsync(String typeName, Guid id)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error reading with typeName: {typeName}, id: {id}.");
_logger.LogError(ex, $"Error reading with typeName: {sanitizedTypeName}, id: {id}.");
return InternalServerError(ex);
}
}

[Route("{typeName}"), HttpGet]
public async Task<IActionResult> ReadAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -159,17 +173,21 @@ public async Task<IActionResult> ReadAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error reading with typeName: {typeName}.");
_logger.LogError(ex, $"Error reading with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("query/{typeName}"), HttpPost]
public async Task<IActionResult> QueryReadAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -222,17 +240,21 @@ public async Task<IActionResult> QueryReadAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error query reading with typeName: {typeName}.");
_logger.LogError(ex, $"Error query reading with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("query/{typeName}/count"), HttpPost]
public async Task<IActionResult> QueryReadCountAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -277,17 +299,21 @@ public async Task<IActionResult> QueryReadCountAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error query reading count with typeName: {typeName}.");
_logger.LogError(ex, $"Error query reading count with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("{typeName}/{id:guid}"), HttpPut]
public async Task<IActionResult> UpdateAsync(String typeName, Guid id)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand All @@ -312,7 +338,7 @@ public async Task<IActionResult> UpdateAsync(String typeName, Guid id)
var updatedModel = await _preserver.UpdateAsync(model, id);

if (updatedModel is null)
return NotFound(String.Format(ErrorMessage.NotFoundUpdate, typeName));
return NotFound(String.Format(ErrorMessage.NotFoundUpdate, sanitizedTypeName));

var postprocessingMessageResult = (MessageResult)await _postprocessingService.PostprocessUpdateAsync(updatedModel, id);
if (!postprocessingMessageResult.IsSuccessful)
Expand All @@ -322,17 +348,21 @@ public async Task<IActionResult> UpdateAsync(String typeName, Guid id)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error updating with typeName: {typeName}, id: {id}.");
_logger.LogError(ex, $"Error updating with typeName: {sanitizedTypeName}, id: {id}.");
return InternalServerError(ex);
}
}

[Route("{typeName}/{id:guid}"), HttpPatch]
public async Task<IActionResult> PartialUpdateAsync(String typeName, Guid id)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand All @@ -359,7 +389,7 @@ public async Task<IActionResult> PartialUpdateAsync(String typeName, Guid id)
var updatedModel = await (dynamic)partialUpdateAsync.Invoke(_preserver, new object[] { id, propertyValues });

if (updatedModel is null)
return NotFound(String.Format(ErrorMessage.NotFoundUpdate, typeName));
return NotFound(String.Format(ErrorMessage.NotFoundUpdate, sanitizedTypeName));

var postprocessingMessageResult = (MessageResult)await _postprocessingService.PostprocessPartialUpdateAsync(updatedModel, id, propertyValues);
if (!postprocessingMessageResult.IsSuccessful)
Expand All @@ -369,17 +399,21 @@ public async Task<IActionResult> PartialUpdateAsync(String typeName, Guid id)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error partially updating with typeName: {typeName}, id {id}.");
_logger.LogError(ex, $"Error partially updating with typeName: {sanitizedTypeName}, id {id}.");
return InternalServerError(ex);
}
}

[Route("{typeName}"), HttpPatch]
public async Task<IActionResult> PartialUpdateAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -415,17 +449,21 @@ public async Task<IActionResult> PartialUpdateAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error partially updating with typeName: {typeName}.");
_logger.LogError(ex, $"Error partially updating with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("{typeName}/{id:guid}"), HttpDelete]
public async Task<IActionResult> DeleteAsync(String typeName, Guid id)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand All @@ -443,7 +481,7 @@ public async Task<IActionResult> DeleteAsync(String typeName, Guid id)
var deletedCount = await (dynamic)deleteAsync.Invoke(_preserver, new object[] { id });

if (deletedCount == 0)
return NotFound(String.Format(ErrorMessage.NotFoundDelete, typeName));
return NotFound(String.Format(ErrorMessage.NotFoundDelete, sanitizedTypeName));

var postprocessingMessageResult = (MessageResult)await _postprocessingService.PostprocessDeleteAsync(model, id, deletedCount);
if (!postprocessingMessageResult.IsSuccessful)
Expand All @@ -453,17 +491,21 @@ public async Task<IActionResult> DeleteAsync(String typeName, Guid id)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error deleting with typeName: {typeName}, id: {id}.");
_logger.LogError(ex, $"Error deleting with typeName: {sanitizedTypeName}, id: {id}.");
return InternalServerError(ex);
}
}

[Route("{typeName}"), HttpDelete]
public async Task<IActionResult> DeleteAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -494,17 +536,21 @@ public async Task<IActionResult> DeleteAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error deleting with typeName: {typeName}.");
_logger.LogError(ex, $"Error deleting with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}

[Route("query/{typeName}"), HttpDelete]
public async Task<IActionResult> QueryDeleteAsync(String typeName)
{
var sanitizedTypeName = Default.TypeName;

try
{
var type = _typeService.GetModelType(typeName);
sanitizedTypeName = _sanitizerService.SanitizeTypeName(typeName);

var type = _typeService.GetModelType(sanitizedTypeName);
if (type is null)
return BadRequest(ErrorMessage.BadRequestModelType);

Expand Down Expand Up @@ -549,7 +595,7 @@ public async Task<IActionResult> QueryDeleteAsync(String typeName)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error deleting with typeName: {typeName}.");
_logger.LogError(ex, $"Error deleting with typeName: {sanitizedTypeName}.");
return InternalServerError(ex);
}
}
Expand Down
4 changes: 2 additions & 2 deletions Crud.Api/Crud.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="MongoDB.Driver" Version="2.21.0" />
<PackageReference Include="MongoDB.Bson" Version="2.21.0" />
<PackageReference Include="MongoDB.Driver" Version="2.22.0" />
<PackageReference Include="MongoDB.Bson" Version="2.22.0" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions Crud.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
builder.Services.AddScoped<ITypeService, TypeService>();
builder.Services.AddScoped<IPreprocessingService, PreprocessingService>();
builder.Services.AddScoped<IPostprocessingService, PostprocessingService>();
builder.Services.AddScoped<ISanitizerService, SanitizerService>();

var conventionPack = new ConventionPack
{
Expand Down
7 changes: 7 additions & 0 deletions Crud.Api/Services/ISanitizerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Crud.Api.Services
{
public interface ISanitizerService
{
String SanitizeTypeName(String? className);
}
}
21 changes: 21 additions & 0 deletions Crud.Api/Services/SanitizerService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.RegularExpressions;
using Crud.Api.Constants;

namespace Crud.Api.Services
{
public class SanitizerService : ISanitizerService
{
public String SanitizeTypeName(String? typeName)
{
if (String.IsNullOrWhiteSpace(typeName))
return Default.TypeName;

string sanitizedTypeName = Regex.Replace(typeName, @"[^@\w]", String.Empty);

if (sanitizedTypeName.Length == 0)
return Default.TypeName;

return sanitizedTypeName;
}
}
}
Loading

0 comments on commit 5fa11ee

Please sign in to comment.