Skip to content

Commit

Permalink
Add serialization security features (#12)
Browse files Browse the repository at this point in the history
* Implemented configuration optoins for serialization. This closes #10

* Implement and verify rules engine for serializer.

* Closes #10.
  • Loading branch information
JeremyLikness committed Sep 8, 2020
1 parent 95e104a commit 03dd3e6
Show file tree
Hide file tree
Showing 44 changed files with 2,466 additions and 53 deletions.
27 changes: 26 additions & 1 deletion Releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,42 @@

[Back to README](./README.md)

- [0.8 Alpha](#08_Alpha)
- [0.8 Alpha](#08-Alpha)

## 0.8 Alpha

- [0.8.6-alpha](#086-alpha)
- [0.8.5-alpha](#085-alpha)
- [0.8.4-alpha](#084-alpha)
- [0.8.3-alpha](#083-alpha)
- [0.8.2-alpha](#082-alpha)
- [0.8.1-alpha](#081-alpha)
- [0.8.0-alpha](#080-alpha)

### 0.8.6-alpha

**_Breaking Change_**

An overload of the [`Serializer`](./docs/api/ExpressionPowerTools.Serialization.Serializer.cs.md) was changed. The non-typed `Deserialize` requires a
`IQueryHost` to understand the type being deserialized, but the typed overload can default to LINQ-to-objects.

**Serialization**

- Added [`ConfigurationBuilder`](./docs/api/ExpressionPowerTools.Serialization.Configuration.ConfigurationBuilder.cs.md) as the
optimal way to build configurations.
- Added [`DefaultConfiguration`](./docs/api/ExpressionPowerTools.Serialization.Configuration.DefaultConfiguration.cs.md) to
allow default configurations.
- Extended [`ReflectionHelper`](./docs/api/ExpressionPowerTools.Serialization.Serializers.ReflectionHelper.cs.md)
to provide ability to match closed members by [finding generic counterparts](./docs/api/ExpressionPowerTools.Serialization.Serializers.ReflectionHelper.FindGenericVersion.m.md).
- Added [`SerializationRule`](./docs/api/ExpressionPowerTools.Serialization.Rules.SerializationRule.cs.md) and
the [`RulesEngine`](./docs/api/ExpressionPowerTools.Serialization.Rules.RulesEngine.cs.md) to build/host/analyze rules.
- Added [`TypeBase`](./docs/api/ExpressionPowerTools.Serialization.Serializers.TypeBase.cs.md) for easy key calculation for types.
- Added [`MemberSelector`](./docs/api/ExpressionPowerTools.Serialization.Rules.MemberSelector`1.cs.md) to provide
ways to specify members when configuring rules using [`SelectorExtensions`](./docs/api/ExpressionPowerTools.Serialization.Extensions.SelectorExtensions.cs.md)
- Added lazy service requests to [`ServiceHost`](./docs/api/ExpressionPowerTools.Core.Dependencies.ServiceHost.cs.md)
- Modified the expressions with methods and members (`BinaryExpression`, `NewExpression`, `MethodCallExpression` and
`MemberAccessExpression`) to make authorization checks
- Added the [`ConfigureRules`](./docs/api/ExpressionPowerTools.Serialization.Serializer.ConfigureRules.m.md) method

### 0.8.5-alpha

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ namespace ExpressionPowerTools.Core.Comparisons
/// </summary>
public static class ExpressionEquivalency
{
/// <summary>
/// A lazy proxy to the rules.
/// </summary>
private static readonly Lazy<IExpressionComparisonRuleProvider> Provider =
ServiceHost.GetLazyService<IExpressionComparisonRuleProvider>();

/// <summary>
/// Gets the configured rule set.
/// </summary>
private static IExpressionComparisonRuleProvider Rules =>
ServiceHost.GetService<IExpressionComparisonRuleProvider>();
Provider.Value;

/// <summary>
/// Determine if a <see cref="Type"/> is equivalent to another type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ namespace ExpressionPowerTools.Core.Comparisons
/// </summary>
public static class ExpressionSimilarity
{
/// <summary>
/// A lazy proxy to the rules.
/// </summary>
private static readonly Lazy<IExpressionComparisonRuleProvider> Provider =
ServiceHost.GetLazyService<IExpressionComparisonRuleProvider>();

/// <summary>
/// Gets the configured rule set.
/// </summary>
private static IExpressionComparisonRuleProvider Rules =>
ServiceHost.GetService<IExpressionComparisonRuleProvider>();
Provider.Value;

/// <summary>
/// Entry for similarity comparisons. Will cast to
Expand Down
32 changes: 32 additions & 0 deletions src/ExpressionPowerTools.Core/Dependencies/ServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License. See LICENSE in the repository root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ExpressionPowerTools.Core.Comparisons;
Expand Down Expand Up @@ -62,6 +63,12 @@ public static class ServiceHost
/// </summary>
private static readonly object MutexLock = new object();

/// <summary>
/// The satellite registrations.
/// </summary>
private static readonly Stack<IDependentServiceRegistration> Satellites =
new Stack<IDependentServiceRegistration>();

/// <summary>
/// A value indicating whether the container has already been configured.
/// </summary>
Expand Down Expand Up @@ -102,6 +109,8 @@ public static void Reset()
RegisterSatellites(register);
});

AfterRegistered();

Monitor.Exit(MutexLock);
}

Expand All @@ -114,6 +123,15 @@ public static void Reset()
public static T GetService<T>(params object[] parameters) =>
Services.GetService<T>(parameters);

/// <summary>
/// Gets a lazy loaded service.
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> of the service.</typeparam>
/// <param name="parameters">The parameters.</param>
/// <returns>The lazy intialization for the service.</returns>
public static Lazy<T> GetLazyService<T>(params object[] parameters) =>
new Lazy<T>(() => GetService<T>(parameters), LazyThreadSafetyMode.PublicationOnly);

/// <summary>
/// Initialize the container. Can only be done once unless
/// <see cref="Reset"/> is called.
Expand Down Expand Up @@ -144,13 +162,26 @@ public static void Initialize(Action<IServiceRegistration> registration)
registration(register);
});
configured = true;
AfterRegistered();
}
finally
{
Monitor.Exit(MutexLock);
}
}

/// <summary>
/// Notifies satellite assemblies registation is done.
/// </summary>
private static void AfterRegistered()
{
while (Satellites.Count > 0)
{
var satellite = Satellites.Pop();
satellite.AfterRegistered();
}
}

/// <summary>
/// Registers the default instances.
/// </summary>
Expand Down Expand Up @@ -181,6 +212,7 @@ private static void RegisterSatellites(IServiceRegistration register)
{
var satelliteRegistration = Activator.CreateInstance(dependent) as IDependentServiceRegistration;
satelliteRegistration.RegisterDefaultServices(register);
Satellites.Push(satelliteRegistration);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ public interface IDependentServiceRegistration
/// </summary>
/// <param name="registration">The <see cref="IServiceRegistration"/>.</param>
void RegisterDefaultServices(IServiceRegistration registration);

/// <summary>
/// Called after registration is complete.
/// </summary>
void AfterRegistered();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Jeremy Likness. All rights reserved.
// Licensed under the MIT License. See LICENSE in the repository root for license information.

using System;
using System.Text.Json;
using ExpressionPowerTools.Serialization.Serializers;
using ExpressionPowerTools.Serialization.Signatures;

namespace ExpressionPowerTools.Serialization.Configuration
{
/// <summary>
/// Configuration builder for wiring up configuration fluently.
/// </summary>
public class ConfigurationBuilder : IConfigurationBuilder
{
/// <summary>
/// The <see cref="SerializationState"/> to build up.
/// </summary>
private readonly SerializationState state = new SerializationState
{
Options = new JsonSerializerOptions
{
IgnoreNullValues = true,
IgnoreReadOnlyProperties = true,
},
CompressTypes = true,
};

/// <summary>
/// Once <see cref="Configure"/> is called, this instance is done.
/// </summary>
private bool isValid = true;

/// <summary>
/// Configure the type compression.
/// </summary>
/// <param name="compressTypes">The flag indicating whether or not to compress types.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public IConfigurationBuilder CompressTypes(bool compressTypes)
{
CheckValidity();
state.CompressTypes = compressTypes;
return this;
}

/// <summary>
/// Configuration complete. Return the <see cref="SerializationState"/>.
/// </summary>
/// <returns>The <see cref="SerializationState"/>.</returns>
public SerializationState Configure()
{
isValid = false;
return state;
}

/// <summary>
/// Sets the <see cref="JsonSerializerOptions"/>.
/// </summary>
/// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
public IConfigurationBuilder WithJsonSerializerOptions(JsonSerializerOptions options)
{
state.Options = options;
return this;
}

/// <summary>
/// Checks whether configuration is still valid.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when already configured.</exception>
private void CheckValidity()
{
if (!isValid)
{
throw new InvalidOperationException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Jeremy Likness. All rights reserved.
// Licensed under the MIT License. See LICENSE in the repository root for license information.

using System;
using ExpressionPowerTools.Core.Dependencies;
using ExpressionPowerTools.Serialization.Serializers;
using ExpressionPowerTools.Serialization.Signatures;

namespace ExpressionPowerTools.Serialization.Configuration
{
/// <summary>
/// The default configuration used when none is explicitly provided.
/// </summary>
public class DefaultConfiguration : IDefaultConfiguration
{
/// <summary>
/// Synchronization.
/// </summary>
private readonly object lockObj = new object();

/// <summary>
/// Holds the default state.
/// </summary>
private SerializationState defaultState;

/// <summary>
/// Gets a new <see cref="SerializationState"/> with default options.
/// </summary>
/// <returns>The <see cref="SerializationState"/>.</returns>
public SerializationState GetDefaultState()
{
if (defaultState == null)
{
lock (lockObj)
{
if (defaultState == null)
{
defaultState = ServiceHost.GetService<IConfigurationBuilder>().Configure();
}
}
}

SerializationState state;

lock (lockObj)
{
state = new SerializationState
{
CompressTypes = defaultState.CompressTypes,
Options = defaultState.Options,
};
}

return state;
}

/// <summary>
/// Uses the configuration builder to configure the default state.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
public void SetDefaultState(Action<IConfigurationBuilder> builder)
{
var config = ServiceHost.GetService<IConfigurationBuilder>();
builder(config);
lock (lockObj)
{
defaultState = config.Configure();
}
}
}
}
Loading

0 comments on commit 03dd3e6

Please sign in to comment.