Skip to content
This repository has been archived by the owner on Oct 19, 2022. It is now read-only.

Commit

Permalink
Add comparer overload to custom data filter and finish ITs
Browse files Browse the repository at this point in the history
  • Loading branch information
nbarbettini committed Sep 13, 2016
1 parent 7d7b307 commit 8feb05c
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/Stormpath.Owin.Abstractions/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"xmlDoc": true
},
"dependencies": {
"Stormpath.Configuration.Abstractions": "6.0.0-beta1",
"Stormpath.SDK.Abstractions": "0.94.0-beta1"
"Stormpath.Configuration.Abstractions": "6.0.0",
"Stormpath.SDK.Abstractions": "0.94.0"
},
"description": "Common components for the Stormpath OWIN middleware library.",
"frameworks": {
Expand Down
64 changes: 64 additions & 0 deletions src/Stormpath.Owin.Middleware/DefaultSmartComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;

namespace Stormpath.Owin.Middleware
{
internal sealed class DefaultSmartComparer : IEqualityComparer<object>
{
public new bool Equals(object x, object y)
{
if (x == null || y == null)
{
return false;
}

if (x.Equals(y))
{
return true;
}

if (x is string && y is string)
{
return CompareStringsOrdinal(x, y);
}

if (IsIntegerLike(x) && IsIntegerLike(y))
{
return IntegersEquals(x, y);
}

return false;
}

public int GetHashCode(object obj)
=> obj.GetHashCode();

/// <summary>
/// By default, always compare strings with an Ordinal comparison.
/// </summary>
/// <param name="x">The first string.</param>
/// <param name="y">The second string.</param>
/// <returns><c>true</c> if the strings are equal.</returns>
private static bool CompareStringsOrdinal(object x, object y)
=> ((string)x).Equals((string)y, StringComparison.Ordinal);

private static bool IsIntegerLike(object value)
=> value is byte || value is short || value is int || value is long;

/// <summary>
/// Compares two integer-like types, regardless of the size of the underlying type.
/// </summary>
/// <remarks>
/// This is necessary because the Stormpath SDK deserializes integers as <c>long</c> under the hood.
/// </remarks>
/// <param name="obj1">An integer-like type.</param>
/// <param name="obj2">An integer-like type.</param>
/// <returns><c>true</c> if the values are identical.</returns>
private static bool IntegersEquals(object obj1, object obj2)
{
var long1 = (long)Convert.ChangeType(obj1, typeof(long));
var long2 = (long)Convert.ChangeType(obj2, typeof(long));
return long1 == long2;
}
}
}
33 changes: 14 additions & 19 deletions src/Stormpath.Owin.Middleware/RequireCustomDataFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Stormpath.Owin.Abstractions;
using Stormpath.SDK.Account;
Expand All @@ -10,40 +11,34 @@ public sealed class RequireCustomDataFilter : IAuthorizationFilter
{
private readonly string _key;
private readonly object _value;
private readonly IEqualityComparer<object> _comparer;

public RequireCustomDataFilter(string key, object value)
: this(key, value, new DefaultSmartComparer())
{
}

public RequireCustomDataFilter(string key, object value, IEqualityComparer<object> comparer)
{
_key = key;
_value = value;
_comparer = comparer;
}

public bool IsAuthorized(IAccount account)
{
var customData = account?.GetCustomData();

if (customData?[_key] == null)
{
return false;
}

return customData[_key].Equals(_value);
return _comparer.Equals(customData?[_key], _value);
}

public async Task<bool> IsAuthorizedAsync(IAccount account, CancellationToken cancellationToken)
{
if (account == null)
{
return false;
}

var customData = await account.GetCustomDataAsync(cancellationToken).ConfigureAwait(false);

if (customData?[_key] == null)
{
return false;
}
var customData = account == null
? null
: await account.GetCustomDataAsync(cancellationToken).ConfigureAwait(false);

return customData[_key].Equals(_value);
return _comparer.Equals(customData?[_key], _value);
}
}
}
2 changes: 1 addition & 1 deletion src/Stormpath.Owin.Middleware/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"target": "project",
"version": "1.5.0-beta4"
},
"Stormpath.SDK": "0.94.0-beta1"
"Stormpath.SDK": "0.94.0"
},
"description": "Stormpath OWIN middleware for .NET.",
"frameworks": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,12 @@ public void ReturnFalseForNonMatchingCustomData()

public static IEnumerable<object[]> MatchingCustomDataCases()
{
yield return new object[] { true };
yield return new object[] { "hello world!" };
yield return new object[] { 1234 };
yield return new object[] {true};
yield return new object[] {"hello world!"};
yield return new object[] {(byte) 123};
yield return new object[] {(short) 1234};
yield return new object[] {1234};
yield return new object[] {(long) 1234};
}

[Theory]
Expand All @@ -122,7 +125,7 @@ public async Task ReturnTrueForMatchingCustomDataAsync(object data)
}
}

[Theory(Skip = "Sync tests don't work")]
[Theory]
[MemberData(nameof(MatchingCustomDataCases))]
public void ReturnTrueForMatchingCustomData(object data)
{
Expand All @@ -140,5 +143,64 @@ public void ReturnTrueForMatchingCustomData(object data)
filter.IsAuthorized(testAccount).Should().BeTrue();
}
}

[Fact]
public async Task ReturnTrueForMatchingCustomDataWithComparerAsync()
{
IAccount testAccount = null;

using (new TestEnvironment(_fixture.Client, async c =>
{
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
testAccount.CustomData["foobar"] = 123.456f;
await testAccount.SaveAsync();
return new IResource[] { testAccount };
}))
{
var filter = new RequireCustomDataFilter("foobar", 123.456f, new RoundingFloatComparer());
(await filter.IsAuthorizedAsync(testAccount, CancellationToken.None)).Should().BeTrue();
}
}

[Fact]
public void ReturnTrueForMatchingCustomDataWithComparer()
{
IAccount testAccount = null;

using (new TestEnvironment(_fixture.Client, async c =>
{
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
testAccount.CustomData["foobar"] = 123.456f;
await testAccount.SaveAsync();
return new IResource[] { testAccount };
}))
{
var filter = new RequireCustomDataFilter("foobar", 123.456f, new RoundingFloatComparer());
filter.IsAuthorized(testAccount).Should().BeTrue();
}
}

public class RoundingFloatComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
double roundedFloat1, roundedFloat2;

try
{
roundedFloat1 = Math.Round((double) Convert.ChangeType(x, typeof(double)), 1);
roundedFloat2 = Math.Round((double) Convert.ChangeType(y, typeof(double)), 1);
}
catch
{
return false;
}

return roundedFloat1 == roundedFloat2;
}

public int GetHashCode(object obj)
=> obj.GetHashCode();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public async Task ReturnTrueForGroupByNameAsync()
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
var group1 = await _fixture.TestDirectory.CreateGroupAsync(group1Name, $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
var group2 = await _fixture.TestDirectory.CreateGroupAsync(group2Name, $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
await testAccount.AddGroupAsync(group1);
await testAccount.AddGroupAsync(group2);
return new IResource[] { testAccount, group1, group2 };
}))
{
Expand All @@ -98,6 +100,8 @@ public void ReturnTrueForGroupByName()
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
var group1 = await _fixture.TestDirectory.CreateGroupAsync(group1Name, $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
var group2 = await _fixture.TestDirectory.CreateGroupAsync(group2Name, $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
await testAccount.AddGroupAsync(group1);
await testAccount.AddGroupAsync(group2);
return new IResource[] { testAccount, group1, group2 };
}))
{
Expand All @@ -123,6 +127,8 @@ public async Task ReturnTrueForGroupByHrefAsync()
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
var group1 = await _fixture.TestDirectory.CreateGroupAsync($"group1-{Guid.NewGuid()}", $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
var group2 = await _fixture.TestDirectory.CreateGroupAsync($"group1-{Guid.NewGuid()}", $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
await testAccount.AddGroupAsync(group1);
await testAccount.AddGroupAsync(group2);
group1Href = group1.Href;
group2Href = group2.Href;
return new IResource[] { testAccount, group1, group2 };
Expand Down Expand Up @@ -150,6 +156,8 @@ public void ReturnTrueForGroupByHref()
testAccount = await _fixture.TestDirectory.CreateAccountAsync(NewTestAccount(c));
var group1 = await _fixture.TestDirectory.CreateGroupAsync($"group1-{Guid.NewGuid()}", $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
var group2 = await _fixture.TestDirectory.CreateGroupAsync($"group1-{Guid.NewGuid()}", $"Stormpath.Owin IT {_fixture.TestInstanceKey}");
await testAccount.AddGroupAsync(group1);
await testAccount.AddGroupAsync(group2);
group1Href = group1.Href;
group2Href = group2.Href;
return new IResource[] { testAccount, group1, group2 };
Expand Down

0 comments on commit 8feb05c

Please sign in to comment.