Skip to content

Commit

Permalink
Implements blocked attachments and allowed and blocked mime types Dyn…
Browse files Browse the repository at this point in the history
  • Loading branch information
jordimontana82 committed Aug 15, 2024
1 parent fe3db51 commit b195f5e
Show file tree
Hide file tree
Showing 8 changed files with 477 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace FakeXrmEasy.Core.FileStorage.Db.Exceptions
{
/// <summary>
/// Exception raised when a file is uploaded and its file extension is blocked by the BlockedAttachment settings
/// </summary>
public class BlockedAttachmentException: Exception
{
/// <summary>
/// Default constructor
/// </summary>
internal BlockedAttachmentException(string fileName) : base($"The attachment with file name '{fileName}' is not a valid file extension type")
{

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

namespace FakeXrmEasy.Core.FileStorage.Db.Exceptions
{
/// <summary>
/// Exception raised when a file is uploaded and its file MIME type is blocked by either the BlockedMimeType or AllowedMimeType settings
/// </summary>
public class BlockedMimeTypeException: Exception
{
/// <summary>
/// Default constructor
/// </summary>
internal BlockedMimeTypeException(string fileName, string mimeType) : base($"The MIME Type '{mimeType}' for file name '{fileName}' is not a valid")
{

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ internal interface IInMemoryFileDbInternal
void AddFile(FileAttachment fileAttachment);
void DeleteFile(string fileId);
List<FileAttachment> GetFilesForTarget(EntityReference target);

OrganizationFileSettings GetOrganizationFileSettings();

bool IsFileExtensionBlocked(string fileName, OrganizationFileSettings fileSettings);
bool IsMimeTypeAllowed(string mimeType, OrganizationFileSettings fileSettings);
}
}
120 changes: 120 additions & 0 deletions src/FakeXrmEasy.Core/FileStorage/Db/InMemoryFileDb.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FakeXrmEasy.Core.Db;
using FakeXrmEasy.Core.FileStorage.Db.Exceptions;
Expand All @@ -21,7 +22,12 @@ internal class InMemoryFileDb : IInMemoryFileDbUploader, IInMemoryFileDbDownload
private readonly InMemoryDb _db;

internal const string FILE_ATTACHMENT_TABLE_NAME = "fileattachment";
private const string ORGANIZATION_TABLE_NAME = "organization";

private const string BLOCKED_ATTACHMENTS_FIELD_NAME = "blockedattachments";
private const string BLOCKED_MIME_TYPES_FIELD_NAME = "blockedmimetypes";
private const string ALLOWED_MIME_TYPES_FIELD_NAME = "allowedmimetypes";

internal InMemoryFileDb(InMemoryDb db)
{
_db = db;
Expand Down Expand Up @@ -76,6 +82,18 @@ public List<FileAttachment> GetAllFiles()

public void AddFile(FileAttachment fileAttachment)
{
var orgFileSettings = GetOrganizationFileSettings();

if (IsFileExtensionBlocked(fileAttachment.FileName, orgFileSettings))
{
throw new BlockedAttachmentException(fileAttachment.FileName);
}

if (!IsMimeTypeAllowed(fileAttachment.MimeType, orgFileSettings))
{
throw new BlockedMimeTypeException(fileAttachment.FileName, fileAttachment.MimeType);
}

var wasAdded = _files.TryAdd(fileAttachment.Id, fileAttachment);
if (!wasAdded)
{
Expand Down Expand Up @@ -107,8 +125,110 @@ public List<FileAttachment> GetFilesForTarget(EntityReference target)
f.Target.Id.Equals(target.Id))
.ToList();
}

public OrganizationFileSettings GetOrganizationFileSettings()
{
var orgFileSettings = new OrganizationFileSettings();

var exists = GetOrgBlockedAttachments(out var orgBlockedAttachments);
if (exists)
{
orgFileSettings.BlockedAttachments = orgBlockedAttachments;
}

exists = GetOrgAllowedMimeTypes(out var allowedMimeTypes);
if (exists)
{
orgFileSettings.AllowedMimeTypes = allowedMimeTypes;
}

exists = GetOrgBlockedMimeTypes(out var blockedMimeTypes);
if (exists)
{
orgFileSettings.BlockedMimeTypes = blockedMimeTypes;
}
return orgFileSettings;
}

private Entity GetOrganization()
{
if (!_db.ContainsTable(ORGANIZATION_TABLE_NAME))
{
return null;
}

var orgTable = _db.GetTable(ORGANIZATION_TABLE_NAME);
return orgTable.Rows.FirstOrDefault();
}

private bool GetOrgBlockedAttachments(out string[] blockedAttachments)
{
return TryGetOrganizationFileSetting(BLOCKED_ATTACHMENTS_FIELD_NAME, out blockedAttachments);
}

private bool GetOrgAllowedMimeTypes(out string[] allowedMimeTypes)
{
return TryGetOrganizationFileSetting(ALLOWED_MIME_TYPES_FIELD_NAME, out allowedMimeTypes);
}

private bool GetOrgBlockedMimeTypes(out string[] blockedMimeTypes)
{
return TryGetOrganizationFileSetting(BLOCKED_MIME_TYPES_FIELD_NAME, out blockedMimeTypes);
}

private bool TryGetOrganizationFileSetting(string settingFieldName, out string[] fileSetting)
{
bool exists = false;
var organization = GetOrganization();
if (organization == null)
{
fileSetting = new string[] { };
return exists;
}

if (organization.Contains(settingFieldName))
{
exists = true;
var fileSettingAttributeValue = (string)organization[settingFieldName];
if (string.IsNullOrWhiteSpace(fileSettingAttributeValue))
{
fileSetting = new string[] { };
return exists;
}
fileSetting = OrganizationFileSettings.FromCommaSeparated(fileSettingAttributeValue);
}
else
{
fileSetting = new string[] { };
}

return exists;
}

public bool IsFileExtensionBlocked(string fileName, OrganizationFileSettings fileSettings)
{
var extension = new FileInfo(fileName).Extension.Substring(1); //ignore the dot
return fileSettings.BlockedAttachments.Contains(extension);
}

public bool IsMimeTypeAllowed(string mimeType, OrganizationFileSettings fileSettings)
{
if (fileSettings.AllowedMimeTypes.Length > 0)
{
return fileSettings.AllowedMimeTypes.Contains(mimeType);
}

if (fileSettings.BlockedMimeTypes.Length > 0)
{
return !fileSettings.BlockedMimeTypes.Contains(mimeType);
}

return true;
}

#endregion


/// <summary>
///
/// </summary>
Expand Down
39 changes: 39 additions & 0 deletions src/FakeXrmEasy.Core/FileStorage/OrganizationFileSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace FakeXrmEasy.Core.FileStorage
{
/// <summary>
/// Contains the current organization settings that are relevant to file storage
/// </summary>
internal class OrganizationFileSettings
{
internal string[] BlockedAttachments { get; set; }
internal string[] AllowedMimeTypes { get; set; }
internal string[] BlockedMimeTypes { get; set; }

private const char separator = ';';

private const string DEFAULT_BLOCKED_ATTACHMENTS =
@"ade;adp;app;asa;ashx;asmx;asp;bas;bat;cdx;cer;chm;class;cmd;com;config;cpl;crt;csh;dll;exe;fxp;hlp;hta;htr;htw;ida;idc;idq;inf;ins;isp;its;jar;js;jse;ksh;lnk;mad;maf;mag;mam;maq;mar;mas;mat;mau;mav;maw;mda;mdb;mde;mdt;mdw;mdz;msc;msh;msh1;msh1xml;msh2;msh2xml;mshxml;msi;msp;mst;ops;pcd;pif;prf;prg;printer;pst;reg;rem;scf;scr;sct;shb;shs;shtm;shtml;soap;stm;tmp;url;vb;vbe;vbs;vsmacros;vss;vst;vsw;ws;wsc;wsf;wsh;svg";

internal OrganizationFileSettings()
{
BlockedAttachments = FromCommaSeparated(DEFAULT_BLOCKED_ATTACHMENTS);
AllowedMimeTypes = new string[] { };
BlockedMimeTypes = new string[] { };
}

internal static string[] FromCommaSeparated(string commaSeparatedValues)
{
if (commaSeparatedValues.IndexOf(separator) >= 0)
{
return commaSeparatedValues.Split(separator);
}

if (!string.IsNullOrWhiteSpace(commaSeparatedValues))
{
return new string[] { commaSeparatedValues };
}

return new string[] { };
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DataverseEntities;
using FakeXrmEasy.Core.Db;
using FakeXrmEasy.Core.FileStorage.Db;
using FakeXrmEasy.Core.FileStorage.Db.Exceptions;
using Microsoft.Xrm.Sdk;
using Xunit;
using FileAttachment = FakeXrmEasy.Core.FileStorage.Db.FileAttachment;

namespace FakeXrmEasy.Core.Tests.FileStorage.Db
{
public class BlockedAttachmentsTests: FakeXrmEasyTestsBase
{
private const string FILE_ATTRIBUTE_NAME = "dv_file";

private readonly InMemoryDb _db;
private readonly InMemoryFileDb _fileDb;
private readonly Entity _entity;
private readonly FileAttachment _file;

private readonly Entity _organization;

private const string BLOCKED_ATTACHMENTS_ATTRIBUTE = "blockedattachments";

public BlockedAttachmentsTests()
{
_db = (_context as XrmFakedContext).Db;
_fileDb = (_context as XrmFakedContext).FileDb;

_entity = new Entity(dv_test.EntityLogicalName)
{
Id = Guid.NewGuid(),
};

_file = new FileAttachment()
{
Id = Guid.NewGuid().ToString(),
MimeType = "application/pdf",
FileName = "TestFile.pdf",
Target = _entity.ToEntityReference(),
AttributeName = FILE_ATTRIBUTE_NAME,
Content = new byte[] { 1, 2, 3, 4 }
};

_organization = new Entity(Organization.EntityLogicalName)
{
Id = Guid.NewGuid()
};
}

[Fact]
public void Should_throw_blocked_attachment_exception_if_a_file_extension_is_not_allowed()
{
_file.FileName = "TestFile.ade";
Assert.Throws<BlockedAttachmentException>(() => _fileDb.AddFile(_file));
}

[Theory]
[InlineData(null, "File.ade")]
[InlineData("asmx", "File.ade")]
[InlineData("asmx;php", "File.ade")]
[InlineData("asmx;php", "File.pdf")]
public void Should_not_throw_blocked_attachment_exception_with_a_custom_blocked_attachments_file_extension(string blockedAttachments, string fileName)
{
_organization[BLOCKED_ATTACHMENTS_ATTRIBUTE] = blockedAttachments;

_context.Initialize(new List<Entity>()
{
_organization, _entity
});

_file.FileName = fileName;
_fileDb.AddFile(_file);

var fileAdded = _fileDb.GetAllFiles().FirstOrDefault(f => f.Id == _file.Id);
Assert.NotNull(fileAdded);
}

[Theory]
[InlineData("asmx", "File.asmx")]
[InlineData("asmx;php", "File.asmx")]
[InlineData("asmx;php", "File.php")]
public void Should_throw_blocked_attachment_exception_with_a_custom_blocked_attachments_file_extension(string blockedAttachments, string fileName)
{
_organization[BLOCKED_ATTACHMENTS_ATTRIBUTE] = blockedAttachments;

_context.Initialize(new List<Entity>()
{
_organization, _entity
});

_file.FileName = fileName;
Assert.Throws<BlockedAttachmentException>(() => _fileDb.AddFile(_file));
}
}
}
Loading

0 comments on commit b195f5e

Please sign in to comment.