Skip to content

Commit

Permalink
Cherry pick agd1300 (#10221)
Browse files Browse the repository at this point in the history
* AGD-1300 Add mechanism for common package directory similar to common definition directory alternative (#10208)

* Add common package folder location similar to common definitions directory to PathManager

* Initalize common package directory

* Add a list of root directories to verify certificates and check those before adding the package

* fix null value check

* Update comments

* Modify package and package loader to only check libraries defined in pkg.json manifest

* Check view extension for valid signed certificate if path matches root to check

* Upate extension loader to validate extension have valid certificate if they are found in directories requiring verification

* Rename package property for to RequiresSignedEntryPoints

* Fix bugs in package loader signed check

* Revise view extension / extension loading logic to avoid ext deff pointing to dll outside of check dir

* Comments

* Package PR comments

* Extension loading pr comments

* View extension pr comments

* On more comment

* Refactor .Net assembly cetificate verication to DynamoCrypto

* Consume validate method from DyanmoCrypto

* Add comments

* Move cert verification method to DynamoUtilities Class

* Add new CertificateVerification class to DynamoUtilities

* Update comments

* Extract package error messages to resource files

* Add comments and change RequiresSignedEntryPoints to internal

* Add test Cases for signed packages

(cherry picked from commit 717bb16)

* AGD-1300 Test Followup (#10211)

* Add untitlity test

(cherry picked from commit 2fcd3b5)

* Fix lib tests

(cherry picked from commit 7ae1302)

* Add check to package property

(cherry picked from commit 8e1bc3c)

* Mark test for failure

(cherry picked from commit 32d7a2d)

* Agd-1300 Turn on valid package test (#10213)

* Update the signed package file to uniqure namespace

(cherry picked from commit 0005f38)

* Add comment including the zt node

(cherry picked from commit 16c28de)

* Update comment

(cherry picked from commit 75346ca)
  • Loading branch information
saintentropy authored and mjkkirschner committed Dec 13, 2019
1 parent afd3a4b commit 3da9f10
Show file tree
Hide file tree
Showing 27 changed files with 465 additions and 27 deletions.
12 changes: 12 additions & 0 deletions src/DynamoCore/Configuration/PathManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class PathManager : IPathManager
private readonly string commonDataDir;

private readonly string commonDefinitions;
private readonly string commonPackages;
private readonly string logDirectory;
private readonly string samplesDirectory;
private readonly string backupDirectory;
Expand All @@ -95,6 +96,15 @@ private IEnumerable<string> RootDirectories
get { return Preferences != null ? Preferences.CustomPackageFolders : rootDirectories; }
}

//Todo in Dynamo 3.0, Add this to the IPathManager interface
/// <summary>
/// The local directory that contains package directory created by all users.
/// </summary>
internal string CommonPackageDirectory
{
get { return commonPackages; }
}

#region IPathManager Interface Implementation

public string DynamoCoreDirectory
Expand Down Expand Up @@ -344,6 +354,7 @@ internal PathManager(PathManagerParams pathManagerParams)
commonDataDir = GetCommonDataFolder(pathResolver);

commonDefinitions = Path.Combine(commonDataDir, DefinitionsDirectoryName);
commonPackages = Path.Combine(commonDataDir, PackagesDirectoryName);
samplesDirectory = GetSamplesFolder(commonDataDir);
var galleryDirectory = GetGalleryDirectory(commonDataDir);
galleryFilePath = Path.Combine(galleryDirectory, GalleryContentsFileName);
Expand Down Expand Up @@ -390,6 +401,7 @@ internal void EnsureDirectoryExistence(List<Exception> exceptions)
// Common data folders for all users.
exceptions.Add(PathHelper.CreateFolderIfNotExist(commonDataDir));
exceptions.Add(PathHelper.CreateFolderIfNotExist(commonDefinitions));
exceptions.Add(PathHelper.CreateFolderIfNotExist(commonPackages));

exceptions.RemoveAll(x => x == null); // Remove all null entries.
}
Expand Down
5 changes: 5 additions & 0 deletions src/DynamoCore/Extensions/ExtensionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public class ExtensionDefinition

[DataMember]
public string TypeName { get; set; }

/// <summary>
/// Is set to true if the ExtensionDefinition is located in a directory that requires certificate verification of its entry point dll.
/// </summary>
internal bool RequiresSignedEntryPoint { get; set; } = false;
}
}
23 changes: 22 additions & 1 deletion src/DynamoCore/Extensions/ExtensionLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using System.Xml;
using Dynamo.Logging;
using DynamoUtilities;

namespace Dynamo.Extensions
{
Expand All @@ -22,9 +23,13 @@ public class ExtensionLoader: IExtensionLoader, ILogSource
{
private IExtension Load(ExtensionDefinition extension)
{

try
{
if (extension.RequiresSignedEntryPoint)
{
CertificateVerification.CheckAssemblyForValidCertificate(extension.AssemblyPath);
}

var assembly = Assembly.LoadFrom(extension.AssemblyPath);
var result = assembly.CreateInstance(extension.TypeName) as IExtension;
ExtensionLoading?.Invoke(result);
Expand Down Expand Up @@ -73,6 +78,15 @@ public IExtension Load(string extensionPath)
}
}

//Check if the extension definition file was located in a directory which requires certificate validation.
foreach (var pathToVerifyCert in DirectoriesToVerifyCertificates)
{
if (extensionPath.Contains(pathToVerifyCert))
{
definition.RequiresSignedEntryPoint = true;
}
}

var extension = Load(definition);
return extension;
}
Expand Down Expand Up @@ -124,5 +138,12 @@ private void Log(string msg)
/// An event that is raised when an extension starts loading.
/// </summary>
public event Action<IExtension> ExtensionLoading;

/// <summary>
/// A list of root directories which require extensions to have a signed entry point
/// File path locations from package definition xml's are validated against this collection
/// </summary>
internal List<string> DirectoriesToVerifyCertificates = new List<string>();

}
}
8 changes: 8 additions & 0 deletions src/DynamoCore/Extensions/ExtensionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public ExtensionManager()
this.ExtensionRemoved += UnsubscribeExtension;
}

/// <summary>
/// Creates ExtensionManager with directories which require package certificate verification.
/// </summary>
public ExtensionManager(IEnumerable<string> directoriesToVerify) :this()
{
this.extensionLoader.DirectoriesToVerifyCertificates.AddRange(directoriesToVerify);
}

private void RequestAddExtensionHandler(dynamic extension)
{
if(extension is IExtension)
Expand Down
7 changes: 6 additions & 1 deletion src/DynamoCore/Models/DynamoModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@ protected DynamoModel(IStartConfiguration config)
var userDataFolder = pathManager.GetUserDataFolder(); // Get the default user data path
AddPackagePath(userDataFolder);

// Make sure that the global package folder is added in the list
var userCommonPackageFolder = pathManager.CommonPackageDirectory;
AddPackagePath(userCommonPackageFolder);

// Load Python Template
// The loading pattern is conducted in the following order
// 1) Set from DynamoSettings.XML
Expand Down Expand Up @@ -690,7 +694,8 @@ protected DynamoModel(IStartConfiguration config)
NodeFactory = new NodeFactory();
NodeFactory.MessageLogged += LogMessage;

extensionManager = new ExtensionManager();
//Initialize the ExtensionManager with the CommonDataDirectory so that extensions found here are checked first for dll's with signed certificates
extensionManager = new ExtensionManager(new[] { PathManager.CommonDataDirectory });
extensionManager.MessageLogged += LogMessage;
var extensions = config.Extensions ?? LoadExtensions();

Expand Down
5 changes: 5 additions & 0 deletions src/DynamoCoreWpf/Extensions/ViewExtensionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ internal class ViewExtensionDefinition

[DataMember]
public string TypeName { get; set; }

/// <summary>
/// Is set to true if the ViewExtensionDefinition is located in a directory that requires certificate verification of its entry point dll.
/// </summary>
internal bool RequiresSignedEntryPoint { get; set; } = false;
}
}
26 changes: 25 additions & 1 deletion src/DynamoCoreWpf/Extensions/ViewExtensionLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using System.Xml;
using Dynamo.Logging;
using DynamoUtilities;

namespace Dynamo.Wpf.Extensions
{
Expand All @@ -13,15 +14,22 @@ private IViewExtension Load(ViewExtensionDefinition viewExtension)
{
try
{
if (viewExtension.RequiresSignedEntryPoint)
{
CertificateVerification.CheckAssemblyForValidCertificate(viewExtension.AssemblyPath);
}

var assembly = Assembly.LoadFrom(viewExtension.AssemblyPath);
var result = assembly.CreateInstance(viewExtension.TypeName) as IViewExtension;
ExtensionLoading?.Invoke(result);
return result;
}
catch
catch(Exception ex)
{
var name = viewExtension.TypeName == null ? "null" : viewExtension.TypeName;
Log("Could not create an instance of " + name);
Log(ex.Message);
Log(ex.StackTrace);
return null;
}
}
Expand Down Expand Up @@ -54,6 +62,15 @@ public IViewExtension Load(string extensionPath)
}
}

//Check if the view extension definition file was located in a directory which requires certificate validation.
foreach (var pathToVerifyCert in DirectoriesToVerifyCertificates)
{
if (extensionPath.Contains(pathToVerifyCert))
{
definition.RequiresSignedEntryPoint = true;
}
}

var extension = Load(definition);
return extension;
}
Expand Down Expand Up @@ -97,5 +114,12 @@ private void Log(string msg)
/// An event that is raised when an extension starts loading.
/// </summary>
public event Action<IViewExtension> ExtensionLoading;

/// <summary>
/// A list of root directories which require extensions to have a signed entry point
/// File path locations from package definition xml's are validated against this collection
/// </summary>
internal List<string> DirectoriesToVerifyCertificates = new List<string>();

}
}
8 changes: 8 additions & 0 deletions src/DynamoCoreWpf/Extensions/ViewExtensionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public ViewExtensionManager()
this.ExtensionRemoved += UnsubscribeViewExtension;
}

/// <summary>
/// Creates ViewExtensionManager with directories which require package certificate verification.
/// </summary>
public ViewExtensionManager(IEnumerable<string> directoriesToVerify) : this()
{
this.viewExtensionLoader.DirectoriesToVerifyCertificates.AddRange(directoriesToVerify);
}

private void RequestAddViewExtensionHandler(IViewExtension viewExtension)
{
if (viewExtension is IViewExtension)
Expand Down
5 changes: 4 additions & 1 deletion src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public partial class DynamoView : Window, IDisposable
private int tabSlidingWindowStart, tabSlidingWindowEnd;
private GalleryView galleryView;
private readonly LoginService loginService;
internal ViewExtensionManager viewExtensionManager = new ViewExtensionManager();
internal ViewExtensionManager viewExtensionManager;
private ShortcutToolbar shortcutBar;
private bool loaded = false;

Expand Down Expand Up @@ -98,6 +98,9 @@ public DynamoView(DynamoViewModel dynamoViewModel)

tabSlidingWindowStart = tabSlidingWindowEnd = 0;

//Initialize the ViewExtensionManager with the CommonDataDirectory so that view extensions found here are checked first for dll's with signed certificates
viewExtensionManager = new ViewExtensionManager(new[] {dynamoViewModel.Model.PathManager.CommonDataDirectory });

_timer = new Stopwatch();
_timer.Start();

Expand Down
63 changes: 53 additions & 10 deletions src/DynamoPackages/Package.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using Dynamo.Core;
using Dynamo.Exceptions;
using Dynamo.Graph.Nodes.CustomNodes;
using Dynamo.Graph.Workspaces;
using Dynamo.Interfaces;
Expand Down Expand Up @@ -139,6 +140,11 @@ internal IEnumerable<Assembly> NodeLibraries
/// </summary>
public PackageUploadRequestBody Header { get; internal set; }

/// <summary>
/// Is set to true if the Package is located in a directory that requires certificate verification of its node library dlls.
/// </summary>
internal bool RequiresSignedEntryPoints { get; set; }

#endregion

public Package(string directory, string name, string versionName, string license)
Expand Down Expand Up @@ -269,20 +275,29 @@ internal IEnumerable<PackageAssembly> EnumerateAssembliesInBinDirectory()
{
Assembly assem;

// dll files may be un-managed, skip those
var result = PackageLoader.TryLoadFrom(assemFile.FullName, out assem);
if (result)
bool shouldLoadFile = true;
if (this.RequiresSignedEntryPoints)
{
// IsNodeLibrary may fail, we store the warnings here and then show
IList<ILogMessage> warnings = new List<ILogMessage>();
shouldLoadFile = IsFileSpecifiedInPackageJsonManifest(nodeLibraries, assemFile.Name, BinaryDirectory);
}

assemblies.Add(new PackageAssembly()
if (shouldLoadFile)
{
// dll files may be un-managed, skip those
var result = PackageLoader.TryLoadFrom(assemFile.FullName, out assem);
if (result)
{
Assembly = assem,
IsNodeLibrary = IsNodeLibrary(nodeLibraries, assem.GetName(), ref warnings)
});
// IsNodeLibrary may fail, we store the warnings here and then show
IList<ILogMessage> warnings = new List<ILogMessage>();

assemblies.Add(new PackageAssembly()
{
Assembly = assem,
IsNodeLibrary = IsNodeLibrary(nodeLibraries, assem.GetName(), ref warnings)
});

warnings.ToList().ForEach(this.Log);
warnings.ToList().ForEach(this.Log);
}
}
}

Expand All @@ -294,6 +309,34 @@ internal IEnumerable<PackageAssembly> EnumerateAssembliesInBinDirectory()
return assemblies;
}

/// <summary>
/// Checks if a specific file is specified in the Node Libraries section of the package manifest json.
/// </summary>
/// <param name="nodeLibraries">node libraries defined in package manifest json.</param>
/// <param name="filename">filename of dll file to check</param>
/// <param name="path">path of the packages</param>
/// <returns></returns>
private static bool IsFileSpecifiedInPackageJsonManifest(IEnumerable<string> nodeLibraries, string filename, string path)
{
foreach (var nodeLibraryAssembly in nodeLibraries)
{
try
{
var name = new AssemblyName(nodeLibraryAssembly).Name + ".dll";
if (name == filename)
{
return true;
}
}
catch
{
throw new LibraryLoadFailedException(path, Resources.IncorrectlyFormattedNodeLibraryWarning);
}
}

return false;
}

/// <summary>
/// Determine if an assembly is in the "node_libraries" list for the package.
///
Expand Down
Loading

0 comments on commit 3da9f10

Please sign in to comment.