Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 627bae9

Browse files
authored
Merge pull request #1268 from json-api-dotnet/harden-attributes
Harden Attr/Relationship attributes against invalid input
2 parents 5301d04 + 338c63e commit 627bae9

File tree

14 files changed

+536
-13
lines changed

14 files changed

+536
-13
lines changed

‎src/JsonApiDotNetCore.Annotations/Resources/Annotations/AttrAttribute.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public AttrCapabilities Capabilities
3131
set => _capabilities = value;
3232
}
3333

34+
/// <inheritdoc />
3435
public override bool Equals(object? obj)
3536
{
3637
if (ReferenceEquals(this, obj))
@@ -48,6 +49,7 @@ public override bool Equals(object? obj)
4849
return Capabilities == other.Capabilities && base.Equals(other);
4950
}
5051

52+
/// <inheritdoc />
5153
public override int GetHashCode()
5254
{
5355
return HashCode.Combine(Capabilities, base.GetHashCode());

‎src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasManyAttribute.cs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections;
12
using JetBrains.Annotations;
23

34
// ReSharper disable NonReadonlyMemberInGetHashCode
@@ -65,6 +66,34 @@ private bool EvaluateIsManyToMany()
6566
return false;
6667
}
6768

69+
/// <inheritdoc />
70+
public override void SetValue(object resource, object? newValue)
71+
{
72+
ArgumentGuard.NotNull(newValue);
73+
AssertIsIdentifiableCollection(newValue);
74+
75+
base.SetValue(resource, newValue);
76+
}
77+
78+
private void AssertIsIdentifiableCollection(object newValue)
79+
{
80+
if (newValue is not IEnumerable enumerable)
81+
{
82+
throw new InvalidOperationException($"Resource of type '{newValue.GetType()}' must be a collection.");
83+
}
84+
85+
foreach (object? element in enumerable)
86+
{
87+
if (element == null)
88+
{
89+
throw new InvalidOperationException("Resource collection must not contain null values.");
90+
}
91+
92+
AssertIsIdentifiable(element);
93+
}
94+
}
95+
96+
/// <inheritdoc />
6897
public override bool Equals(object? obj)
6998
{
7099
if (ReferenceEquals(this, obj))
@@ -82,6 +111,7 @@ public override bool Equals(object? obj)
82111
return _capabilities == other._capabilities && base.Equals(other);
83112
}
84113

114+
/// <inheritdoc />
85115
public override int GetHashCode()
86116
{
87117
return HashCode.Combine(_capabilities, base.GetHashCode());

‎src/JsonApiDotNetCore.Annotations/Resources/Annotations/HasOneAttribute.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ private bool EvaluateIsOneToOne()
6464
return false;
6565
}
6666

67+
/// <inheritdoc />
68+
public override void SetValue(object resource, object? newValue)
69+
{
70+
AssertIsIdentifiable(newValue);
71+
base.SetValue(resource, newValue);
72+
}
73+
74+
/// <inheritdoc />
6775
public override bool Equals(object? obj)
6876
{
6977
if (ReferenceEquals(this, obj))
@@ -81,6 +89,7 @@ public override bool Equals(object? obj)
8189
return _capabilities == other._capabilities && base.Equals(other);
8290
}
8391

92+
/// <inheritdoc />
8493
public override int GetHashCode()
8594
{
8695
return HashCode.Combine(_capabilities, base.GetHashCode());

‎src/JsonApiDotNetCore.Annotations/Resources/Annotations/RelationshipAttribute.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public bool CanInclude
8686
set => _canInclude = value;
8787
}
8888

89+
/// <inheritdoc />
8990
public override bool Equals(object? obj)
9091
{
9192
if (ReferenceEquals(this, obj))
@@ -103,6 +104,7 @@ public override bool Equals(object? obj)
103104
return _rightType?.ClrType == other._rightType?.ClrType && Links == other.Links && base.Equals(other);
104105
}
105106

107+
/// <inheritdoc />
106108
public override int GetHashCode()
107109
{
108110
return HashCode.Combine(_rightType?.ClrType, Links, base.GetHashCode());

‎src/JsonApiDotNetCore.Annotations/Resources/Annotations/ResourceFieldAttribute.cs‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ internal set
6868
public object? GetValue(object resource)
6969
{
7070
ArgumentGuard.NotNull(resource);
71+
AssertIsIdentifiable(resource);
7172

7273
if (Property.GetMethod == null)
7374
{
@@ -82,17 +83,18 @@ internal set
8283
{
8384
throw new InvalidOperationException(
8485
$"Unable to get property value of '{Property.DeclaringType!.Name}.{Property.Name}' on instance of type '{resource.GetType().Name}'.",
85-
exception);
86+
exception.InnerException??exception);
8687
}
8788
}
8889

8990
/// <summary>
9091
/// Sets the value of this field on the specified resource instance. Throws if the property is read-only or if the field does not belong to the specified
9192
/// resource instance.
9293
/// </summary>
93-
public void SetValue(object resource, object? newValue)
94+
public virtualvoid SetValue(object resource, object? newValue)
9495
{
9596
ArgumentGuard.NotNull(resource);
97+
AssertIsIdentifiable(resource);
9698

9799
if (Property.SetMethod == null)
98100
{
@@ -107,15 +109,25 @@ public void SetValue(object resource, object? newValue)
107109
{
108110
throw new InvalidOperationException(
109111
$"Unable to set property value of '{Property.DeclaringType!.Name}.{Property.Name}' on instance of type '{resource.GetType().Name}'.",
110-
exception);
112+
exception.InnerException??exception);
111113
}
112114
}
113115

116+
protected void AssertIsIdentifiable(object? resource)
117+
{
118+
if (resource != null && resource is not IIdentifiable)
119+
{
120+
throw new InvalidOperationException($"Resource of type '{resource.GetType()}' does not implement {nameof(IIdentifiable)}.");
121+
}
122+
}
123+
124+
/// <inheritdoc />
114125
public override string? ToString()
115126
{
116127
return _publicName ?? (_property != null ? _property.Name : base.ToString());
117128
}
118129

130+
/// <inheritdoc />
119131
public override bool Equals(object? obj)
120132
{
121133
if (ReferenceEquals(this, obj))
@@ -133,6 +145,7 @@ public override bool Equals(object? obj)
133145
return _publicName == other._publicName && _property == other._property;
134146
}
135147

148+
/// <inheritdoc />
136149
public override int GetHashCode()
137150
{
138151
return HashCode.Combine(_publicName, _property);

‎src/JsonApiDotNetCore/Queries/FieldSelectors.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ public void IncludeAttributes(IEnumerable<AttrAttribute> attributes)
5252
}
5353
}
5454

55-
public void IncludeRelationship(RelationshipAttribute relationship, QueryLayer? queryLayer)
55+
public void IncludeRelationship(RelationshipAttribute relationship, QueryLayer queryLayer)
5656
{
5757
ArgumentGuard.NotNull(relationship);
58+
ArgumentGuard.NotNull(queryLayer);
5859

5960
this[relationship] = queryLayer;
6061
}

‎src/JsonApiDotNetCore/Queries/Internal/Parsing/FilterParser.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ private object ConvertStringToType(string value, Type type)
420420

421421
private Converter<string, object> GetConstantValueConverterForAttribute(AttrAttribute attribute)
422422
{
423-
return stringValue => attribute.Property.Name == nameof(IIdentifiable<object>.Id)
423+
return stringValue => attribute.Property.Name == nameof(Identifiable<object>.Id)
424424
? DeObfuscateStringId(attribute.Type.ClrType, stringValue)
425425
: ConvertStringToType(stringValue, attribute.Property.PropertyType);
426426
}

‎src/JsonApiDotNetCore/Queries/Internal/QueryableBuilding/SelectClauseBuilder.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ private void IncludeAllScalarProperties(Type elementType, Dictionary<PropertyInf
162162

163163
private static void IncludeFields(FieldSelectors fieldSelectors, Dictionary<PropertyInfo, PropertySelector> propertySelectors)
164164
{
165-
foreach ((ResourceFieldAttribute resourceField, QueryLayer? queryLayer) in fieldSelectors)
165+
foreach ((ResourceFieldAttribute resourceField, QueryLayer? nextLayer) in fieldSelectors)
166166
{
167-
var propertySelector = new PropertySelector(resourceField.Property, queryLayer);
167+
var propertySelector = new PropertySelector(resourceField.Property, nextLayer);
168168
IncludeWritableProperty(propertySelector, propertySelectors);
169169
}
170170
}

‎src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ public virtual Task<TResource> GetForCreateAsync(Type resourceClrType, TId id, C
163163
id
164164
});
165165

166+
ArgumentGuard.NotNull(resourceClrType);
167+
166168
var resource = (TResource)_resourceFactory.CreateInstance(resourceClrType);
167169
resource.Id = id;
168170

‎test/JsonApiDotNetCoreTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ public async Task Cannot_select_ToMany_relationship_with_blocked_capability()
791791
}
792792

793793
[Fact]
794-
public async Task Retrieves_all_properties_when_fieldset_contains_readonly_attribute()
794+
public async Task Fetches_all_scalar_properties_when_fieldset_contains_readonly_attribute()
795795
{
796796
// Arrange
797797
var store = _testContext.Factory.Services.GetRequiredService<ResourceCaptureStore>();

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /