Multiplatform Path Validation

There is a very general problem in which it is necessary to check if a path exists, and whether the application can write or read to that path. This problem is different depending on the platform.

Important Questions

These questions drive this research:

  1. Is there a third party NuGet we can use to perform these validations? R\ We have not found any yet, and it seems unlikely given there is not a standard way of solving this problem, according to multiple discussions in the Internet.

  2. Is there a better way to perform the validations? R\ It seems there is not a better way when it comes to checking the Write and Read permissions.

  3. Can we refactor this code to make it better? R\ We intend to move this component to Generic.Utils and change the FileSystemValidator into an abstract class. We also intend to change the current validation flow in order to replace the multiple calls from the UI with a single class for each case (output path validation and input path validation).

There are also questions related to the refactoring of the code:

  1. Can we check this with a single call? R\ Yes. A call for the input path and a call for the output path will be enough.

  2. There are validations in the UI: can we move them to the controller? R\ Yes, we can. However, it is not necessarily the better option.

  3. How many validations are we going to make. R\ Currently we want all validations always.

These questions are better answered in the Current Validation Flow section and the Proposed Validation Flow section.

Current Validation Flow

In the main process of the UI (Electron/Node), there are multiple handlers reacting to events that can be sent from the renderer process (Angular/Typescript):

this.ipcMainHandler.registerHandler(Messages.Validation.InputFilesExtensions,
        this.validateInputFilesExtensions.bind(this));
this.ipcMainHandler.registerHandler(Messages.Validation.InputFileExtension,
        this.validateInputFileExtension.bind(this));
this.ipcMainHandler.registerHandler(Messages.Validation.FolderWritePermissions,
        this.validateFolderWritePermissions.bind(this));
this.ipcMainHandler.registerHandler(Messages.Validation.FolderReadPermissions, 
        this.validateFolderReadPermissions.bind(this));
this.ipcMainHandler.registerHandler(Messages.Validation.EmptyFolder,
        this.validateEmptyOutputFolder.bind(this));

Each of these handlers will send a request to the controller process (C#). Below, an example is presented:

async validateFolderReadPermissions(event: string, args: any[]) {
    const result = await this.connection.sendRequest(
        Messages.Validation.FolderReadPermissions, args);
    return result;
}

The other handlers are identical to the one above, except for the name of the method and the message that is sent.

The C# process is subscribed to these events and reacts to each of them, calling the FileSystemValidator from the Controller.

Validations made in the front-end for input and output paths

  • Paths are non-empty

  • Input and output paths are different

  • Paths are not network paths

  • Paths have the correct syntax (different regular expression for Windows and Unix)

  • Existing path (input only)

  • Path length is less than 260 characters

Maximum path length is not 260 characters in Unix

Proposed Validation Flow

The proposal is to validate the input and output paths with a single call to the Controller, instead of having multiple validations, some in the front-end and some in the back-end. This single call should also return a FileSystemValidationStatus object which will provide the necessary information for the UI to react.

Maybe some validations should be performed in the front-end for performance reasons. However, right now it does not seem like any of the validations fall in this category.

Validations

Moved from the UI to the Controller

  • Paths are non-empty (repeat just before starting the execution?)

  • Input and output paths are different (in the Generic UI?)

  • Paths are not network paths (should we allow network paths?) [Useful link]

  • Paths have the correct syntax

Originally in the Controller

  • Read Permission

  • Write Permission

  • Invalid Extensions (should we keep this?)

  • Directory exists

  • Directory is not empty

Removed from the UI

  • Path length (duplicated in the Controller)

  • Existing path (duplicated in the Controller)

The Supported Extensions Validation (?): should this validation be done every time the path is modified?

Proposed Sequence

The next sequence diagram explains four events:

  • Initialization

  • Set Input Folder

  • Set Output Folder

  • Execute: It is important to validate at some moment

Moving this logic to Generic.Utils

This problem seems generic enough to be included in the Generic.Utils repo instead of being handled in the Common.Runner/Controller. The proposal is to create the necessary code in the Generic.Utils and including the module in the Controller.

Having an implementation for every supported platform

In order to separate the logic needed for every supported platform, it would be useful to have an Abstract Class which defines common methods that should be implemented/overriden in Concrete Classes: one for every platform. This would result in

  • FileSystemValidator (Abstract)

  • WindowsFileSystemValidator (Concrete)

  • UnixFileSystemValidator (Concrete)

There should also be a class which is responsible for the instantiation of the appropriate class (FileSystemValidatorFactory or similar) depending on the operating system.

Using fs from Node.js

This approach would probably only be effective for the Common.Runner/Controller because of its ability to communicate with a Node process. As of now, this approach has been discarded.

It is possible to use the fs module for node and make the validations in the node process instead of in the controller, altough this would probably be more complicated.

Solutions for each Platform

We must consider both .Net Core and .Net Framework

Currently, the validation for the Write permission is checked by writing a file and then deleting it.

The validation for the Read permission is computed using a mechanism similar to the one described in the first link. However, there is no current validation for the Allow Read access, the code is only checking if there is no Deny Read access that applies.

Edge Cases

  • A subfolder has Deny Read

  • Path.GetFullPath has different behavior in .Net Core and .Net Framework

Last updated