Skip to content

Commit

Permalink
feat: #379 allow global suppression of auto-includes
Browse files Browse the repository at this point in the history
  • Loading branch information
ascott18 committed Apr 10, 2024
1 parent 4f4aece commit 0ec0bac
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 3 deletions.
3 changes: 2 additions & 1 deletion docs/modeling/model-components/data-sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ When an object or list of objects is requested, the default behavior of the the

Clients can suppress this per-request by setting `includes = "none"` on your TypeScript [ViewModel](/stacks/disambiguation/view-model.md) or [ListViewModel](/stacks/disambiguation/list-view-model.md), but note this is not a security mechanism and should only be used to reduce payload size or improve response time.

On the server, you can suppress this behavior by placing `[Read(NoAutoInclude = true)]` on either an entire class (affecting all navigation properties of that type), or on specific navigation properties. When placed on a entity class that holds sensitive data, this can help ensure you don't accidentally leak records due to forgetting to customize the data sources of the types whose navigation properties reference your sensitive entity.
On the server, you can suppress this behavior by placing `[Read(NoAutoInclude = true)]` on either an entire class (affecting all navigation properties of that type), or on specific navigation properties. When placed on a entity class that holds sensitive data, this can help ensure you don't accidentally leak records due to forgetting to customize the data sources of the types whose navigation properties reference your sensitive entity.

You can also suppress this for your entire application by placing `[assembly: CoalesceConfiguration(NoAutoInclude = true)]` on the assembly that holds your models.


### Properties
Expand Down
1 change: 1 addition & 0 deletions docs/topics/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Prevent

* Omit `base` call in Data Source `GetQuery` override.
* `[Read(NoAutoInclude = true)]` on properties or types.
* `[assembly: CoalesceConfiguration(NoAutoInclude = true)]`

</td>
</tr>
Expand Down
2 changes: 2 additions & 0 deletions playground/Coalesce.Domain/AppDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System;
using System.Linq;

// [assembly: CoalesceConfiguration(NoAutoInclude = true)]

namespace Coalesce.Domain
{

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace IntelliTect.Coalesce
{
/// <summary>
/// Defines static, assembly-level configuration for the Coalesce models in the targeted assembly.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class CoalesceConfigurationAttribute : Attribute
{
/// <summary>
/// <para>
/// If true, models defined in the targeted assembly will not be loaded by Coalesce's
/// <see cref="QueryableExtensions.IncludeChildren"/> method,
/// nor by the <a href="https://intellitect.github.io/Coalesce/modeling/model-components/data-sources.html#default-loading-behavior">Default Loading Behavior</a>,
/// which normally automatically includes the first level of navigation properties of any entity returned from a Coalesce-generated /get, /list, or /save API endpoint.
/// </para>
/// <para>
/// If true, you must always Include the desired navigation properties in a custom data source, or load entites directly from the type's /get or /list endpoints.
/// </para>
/// <para>
/// Use this to prevent accidental unrestricted, unfiltered loading of types that hold sensitive information.</para>
/// </summary>
public bool NoAutoInclude { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/IntelliTect.Coalesce/Helpers/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class QueryableExtensions
{
/// <summary>
/// <para>Includes immediate children, as well as the other side of many-to-many relationships.</para>
/// <para>Does not include navigations or classes that have <see cref="ReadAttribute.NoAutoInclude"/> set.</para>
/// <para>Does not include navigations or classes that have <see cref="ReadAttribute.NoAutoInclude"/> or <see cref="CoalesceConfigurationAttribute.NoAutoInclude"/> set.</para>
/// </summary>
public static IQueryable<T> IncludeChildren<T>(this IQueryable<T> query, ReflectionRepository? reflectionRepository = null) where T : class
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ public ReflectionAttributeViewModel(TAttribute instance, ReflectionRepository? r
}
}

internal class ReflectionAttributeProvider(ICustomAttributeProvider symbol) : IAttributeProvider
{
public IEnumerable<AttributeViewModel<TAttribute>> GetAttributes<TAttribute>()
where TAttribute : Attribute
=> symbol.GetAttributes<TAttribute>();
}

public static class ReflectionExtensions
{
public static IEnumerable<ReflectionAttributeViewModel<TAttribute>> GetAttributes<TAttribute>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public SymbolAttributeViewModel(AttributeData attributeData, ReflectionRepositor
}
}

public class SymbolAttributeProvider(ISymbol symbol) : IAttributeProvider
internal class SymbolAttributeProvider(ISymbol symbol) : IAttributeProvider
{
public IEnumerable<AttributeViewModel<TAttribute>> GetAttributes<TAttribute>()
where TAttribute : Attribute
Expand Down
5 changes: 5 additions & 0 deletions src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,11 @@ public bool CanAutoInclude
return false;
}

if (PureType.Assembly.GetAttributeValue<CoalesceConfigurationAttribute, bool>(a => a.NoAutoInclude) == true)
{
return false;
}

return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ internal static ReflectionTypeViewModel GetOrCreate(ReflectionRepository? reflec
return reflectionRepository?.GetOrAddType(type) ?? new ReflectionTypeViewModel(reflectionRepository, type);
}

public override IAttributeProvider Assembly
=> new ReflectionAttributeProvider(Info.Assembly);

// TODO: why is an arity of 1 removed from the name? Seems to be an oversight
// - If we're removing arity, we should remove any arity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ internal static SymbolTypeViewModel GetOrCreate(ReflectionRepository? reflection
return reflectionRepository?.GetOrAddType(symbol) ?? new SymbolTypeViewModel(reflectionRepository, symbol);
}

public override IAttributeProvider Assembly
=> Symbol.ContainingAssembly.GetAttributeProvider();

protected override bool ShouldCreateClassViewModel
=> base.ShouldCreateClassViewModel && Symbol is INamedTypeSymbol;

Expand Down
2 changes: 2 additions & 0 deletions src/IntelliTect.Coalesce/TypeDefinition/TypeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal TypeViewModel(ReflectionRepository? reflectionRepository) : this()

public ReflectionRepository? ReflectionRepository { get; internal set; }

public abstract IAttributeProvider Assembly { get; }

public abstract string Name { get; }

/// <summary>
Expand Down

0 comments on commit 0ec0bac

Please sign in to comment.