-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve GetJsonPath(Expression) and fix some sonarcloud issues Improve test coverage Full validation rewrite complete and existing tests work Probably still some issues and test coverage was spoty so more tests needs to be written. Make IInstanceValidator obsolete Add DataType as input to validators Fix a few issues to prepare for tests of expressions Add more tests
- Loading branch information
Showing
39 changed files
with
2,238 additions
and
890 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using Altinn.App.Core.Models.Validation; | ||
using Altinn.Platform.Storage.Interface.Models; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Altinn.App.Core.Features; | ||
|
||
/// <summary> | ||
/// Interface for handling validation of form data. | ||
/// (i.e. dataElements with AppLogic defined | ||
/// </summary> | ||
public interface IFormDataValidator | ||
{ | ||
/// <summary> | ||
/// The data type this validator is for. Typically either hard coded by implementation or | ||
/// or set by constructor using a <see cref="ServiceKeyAttribute" /> and a keyed service. | ||
/// </summary> | ||
string DataType { get; } | ||
|
||
/// <summary> | ||
/// Extension point if the validator should run for multiple data types. | ||
/// Typical users will just set the <see cref="DataType"/> property. | ||
/// </summary> | ||
/// <param name="dataType">The ID of the data type that might be validated</param> | ||
bool CanValidateDataType(string dataType) => DataType == dataType; | ||
|
||
/// <summary> | ||
/// Used for partial validation to ensure that the validator only runs when relevant fields have changed. | ||
/// </summary> | ||
/// <param name="changedFields">List of the json path to all changed fields for incremental validation</param> | ||
bool ShouldRunForIncrementalValidation(List<string>? changedFields = null); | ||
|
||
/// <summary> | ||
/// Returns the group id of the validator. This is used to run partial validations on the backend. | ||
/// </summary> | ||
/// <remarks> | ||
/// The default implementation should work for most cases. | ||
/// </remarks> | ||
public string? Code => $"{this.GetType().FullName}-{DataType}"; | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="instance"></param> | ||
/// <param name="dataElement"></param> | ||
/// <param name="data"></param> | ||
/// <param name="changedFields"></param> | ||
/// <returns>List of validation issues</returns> | ||
Task<List<ValidationIssue>> ValidateFormData(Instance instance, DataElement dataElement, object data, List<string>? changedFields = null); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using Altinn.App.Core.Models.Validation; | ||
using Altinn.Platform.Storage.Interface.Models; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Altinn.App.Core.Features; | ||
|
||
/// <summary> | ||
/// Interface for handling validation of tasks. | ||
/// </summary> | ||
public interface ITaskValidator | ||
{ | ||
/// <summary> | ||
/// The task id this validator is for. Typically either hard coded by implementation or | ||
/// or set by constructor using a <see cref="ServiceKeyAttribute" /> and a keyed service. | ||
/// </summary> | ||
/// <example> | ||
/// <code> | ||
/// string TaskId { get; init; } | ||
/// // constructor | ||
/// public MyTaskValidator([ServiceKey] string taskId) | ||
/// { | ||
/// TaskId = taskId; | ||
/// } | ||
/// </code> | ||
/// </example> | ||
string TaskId { get; } | ||
|
||
/// <summary> | ||
/// Unique code for the validator. Used to run partial validations on the backend. | ||
/// </summary> | ||
public string Code => this.GetType().FullName ?? string.Empty; | ||
|
||
/// <summary> | ||
/// Actual validation logic for the task | ||
/// </summary> | ||
/// <param name="instance">The instance to validate</param> | ||
/// <returns>List of validation issues to add to this task validation</returns> | ||
Task<List<ValidationIssue>> ValidateTask(Instance instance); | ||
} |
71 changes: 71 additions & 0 deletions
71
src/Altinn.App.Core/Features/Validation/Default/DataAnnotationValidator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
using Altinn.App.Core.Configuration; | ||
using Altinn.App.Core.Features.Validation.Helpers; | ||
using Altinn.App.Core.Models.Validation; | ||
using Altinn.Platform.Storage.Interface.Models; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Abstractions; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Altinn.App.Core.Features.Validation.Default; | ||
|
||
/// <summary> | ||
/// Runs <see cref="System.ComponentModel.DataAnnotations"/> validation on the data object. | ||
/// </summary> | ||
public class DataAnnotationValidator : IFormDataValidator | ||
{ | ||
private readonly IHttpContextAccessor _httpContextAccessor; | ||
private readonly IObjectModelValidator _objectModelValidator; | ||
private readonly GeneralSettings _generalSettings; | ||
|
||
/// <summary> | ||
/// Constructor | ||
/// </summary> | ||
public DataAnnotationValidator([ServiceKey] string dataType, IHttpContextAccessor httpContextAccessor, IObjectModelValidator objectModelValidator, IOptions<GeneralSettings> generalSettings) | ||
{ | ||
DataType = dataType; | ||
_httpContextAccessor = httpContextAccessor; | ||
_objectModelValidator = objectModelValidator; | ||
_generalSettings = generalSettings.Value; | ||
} | ||
|
||
/// <summary> | ||
/// Dummy implementation to satisfy interface, We use <see cref="CanValidateDataType" /> instead | ||
/// </summary> | ||
public string DataType { get; } | ||
|
||
/// <summary> | ||
/// Run validator for all data types. | ||
/// </summary> | ||
public bool CanValidateDataType(string dataType) => true; | ||
|
||
/// <summary> | ||
/// Disable incremental validation for this validator. | ||
/// </summary> | ||
public bool ShouldRunForIncrementalValidation(List<string>? changedFields = null) => false; | ||
|
||
/// <inheritdoc /> | ||
public Task<List<ValidationIssue>> ValidateFormData(Instance instance, DataElement dataElement, object data, List<string>? changedFields = null) | ||
{ | ||
try | ||
{ | ||
var modelState = new ModelStateDictionary(); | ||
var actionContext = new ActionContext( | ||
_httpContextAccessor.HttpContext!, | ||
new Microsoft.AspNetCore.Routing.RouteData(), | ||
new ActionDescriptor(), | ||
modelState); | ||
ValidationStateDictionary validationState = new ValidationStateDictionary(); | ||
_objectModelValidator.Validate(actionContext, validationState, null!, data); | ||
|
||
return Task.FromResult(ModelStateHelpers.ModelStateToIssueList(modelState, instance, dataElement, _generalSettings, data.GetType(), ValidationIssueSources.ModelState)); | ||
} | ||
catch (Exception e) | ||
{ | ||
return Task.FromException<List<ValidationIssue>>(e); | ||
} | ||
Check notice Code scanning / CodeQL Generic catch clause Note
Generic catch clause.
|
||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
src/Altinn.App.Core/Features/Validation/Default/DefaultDataElementValidation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using Altinn.App.Core.Models.Validation; | ||
using Altinn.Platform.Storage.Interface.Enums; | ||
using Altinn.Platform.Storage.Interface.Models; | ||
|
||
namespace Altinn.App.Core.Features.Validation.Default; | ||
|
||
/// <summary> | ||
/// Default validations that run on all data elements to validate metadata and file scan results. | ||
/// </summary> | ||
public class DefaultDataElementValidation : IDataElementValidator | ||
{ | ||
public string DataType { get; } | ||
|
||
/// <summary> | ||
/// Runs on all data elements to validate metadata and file scan results. | ||
/// </summary> | ||
public bool CanValidateDataType(DataType dataType) => true; | ||
|
||
public Task<List<ValidationIssue>> ValidateDataElement(Instance instance, DataElement dataElement, DataType dataType) | ||
{ | ||
var issues = new List<ValidationIssue>(); | ||
if (dataElement.ContentType == null) | ||
{ | ||
issues.Add( new ValidationIssue | ||
{ | ||
InstanceId = instance.Id, | ||
Code = ValidationIssueCodes.DataElementCodes.MissingContentType, | ||
DataElementId = dataElement.Id, | ||
Severity = ValidationIssueSeverity.Error, | ||
Description = ValidationIssueCodes.DataElementCodes.MissingContentType | ||
}); | ||
} | ||
else | ||
{ | ||
var contentTypeWithoutEncoding = dataElement.ContentType.Split(";")[0]; | ||
|
||
if (dataType.AllowedContentTypes != null && dataType.AllowedContentTypes.Count > 0 && | ||
dataType.AllowedContentTypes.All(ct => | ||
!ct.Equals(contentTypeWithoutEncoding, StringComparison.OrdinalIgnoreCase))) | ||
{ | ||
issues.Add( new ValidationIssue | ||
{ | ||
InstanceId = instance.Id, | ||
DataElementId = dataElement.Id, | ||
Code = ValidationIssueCodes.DataElementCodes.ContentTypeNotAllowed, | ||
Severity = ValidationIssueSeverity.Error, | ||
Description = ValidationIssueCodes.DataElementCodes.ContentTypeNotAllowed, | ||
Field = dataType.Id | ||
}); | ||
} | ||
} | ||
|
||
if (dataType.MaxSize.HasValue && dataType.MaxSize > 0 && | ||
(long)dataType.MaxSize * 1024 * 1024 < dataElement.Size) | ||
{ | ||
issues.Add( new ValidationIssue | ||
{ | ||
InstanceId = instance.Id, | ||
DataElementId = dataElement.Id, | ||
Code = ValidationIssueCodes.DataElementCodes.DataElementTooLarge, | ||
Severity = ValidationIssueSeverity.Error, | ||
Description = ValidationIssueCodes.DataElementCodes.DataElementTooLarge, | ||
Field = dataType.Id | ||
}); | ||
} | ||
|
||
if (dataType.EnableFileScan && dataElement.FileScanResult == FileScanResult.Infected) | ||
{ | ||
issues.Add( new ValidationIssue | ||
{ | ||
InstanceId = instance.Id, | ||
DataElementId = dataElement.Id, | ||
Code = ValidationIssueCodes.DataElementCodes.DataElementFileInfected, | ||
Severity = ValidationIssueSeverity.Error, | ||
Description = ValidationIssueCodes.DataElementCodes.DataElementFileInfected, | ||
Field = dataType.Id | ||
}); | ||
} | ||
|
||
if (dataType.EnableFileScan && dataType.ValidationErrorOnPendingFileScan && | ||
dataElement.FileScanResult == FileScanResult.Pending) | ||
{ | ||
issues.Add( new ValidationIssue | ||
{ | ||
InstanceId = instance.Id, | ||
DataElementId = dataElement.Id, | ||
Code = ValidationIssueCodes.DataElementCodes.DataElementFileScanPending, | ||
Severity = ValidationIssueSeverity.Error, | ||
Description = ValidationIssueCodes.DataElementCodes.DataElementFileScanPending, | ||
Field = dataType.Id | ||
}); | ||
} | ||
|
||
return Task.FromResult(issues); | ||
} | ||
} |
Oops, something went wrong.