-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding case in-/sensitivity option to header and query keys
- Loading branch information
Showing
7 changed files
with
324 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
namespace Dnmh.Security.ApiKeyAuthentication.AuthenticationHandler.Internal; | ||
|
||
/// <summary> | ||
/// Extension methods for <see cref="Key"/> | ||
/// </summary> | ||
internal static class KeyExtensions | ||
{ | ||
/// <summary> | ||
/// Intersects an <see cref="IEnumerable{T}"/> with type <see cref="Key"/> and an <see cref="IEnumerable{T}"/> with type <see cref="string"/> using each key's <see cref="Key.StringComparer"/> | ||
/// </summary> | ||
public static IEnumerable<Key> Intersect(this IEnumerable<Key> first, IEnumerable<string> second) | ||
{ | ||
ArgumentNullException.ThrowIfNull(first, nameof(first)); | ||
ArgumentNullException.ThrowIfNull(second, nameof(second)); | ||
|
||
return first.Where(x => second.Contains(x.Name, x.StringComparer)); | ||
} | ||
|
||
/// <summary> | ||
/// Determines if an <see cref="IEnumerable{T}"/> with type <see cref="Key"/> contains the given <paramref name="value"/> using each key's <see cref="Key.StringComparer"/> | ||
/// </summary> | ||
public static bool Contains(this IEnumerable<Key> keys, string value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(keys, nameof(keys)); | ||
|
||
foreach (var key in keys) | ||
{ | ||
if (key.StringComparer.Equals(key.Name, value)) | ||
{ | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
namespace Dnmh.Security.ApiKeyAuthentication.AuthenticationHandler; | ||
|
||
/// <summary> | ||
/// A key with a string value and a case in-/sensitive boolean value | ||
/// </summary> | ||
public class Key : IEquatable<Key> | ||
{ | ||
/// <summary> | ||
/// The name of the key | ||
/// </summary> | ||
public string Name { get; } | ||
|
||
/// <summary> | ||
/// Indicates if the key is case sensitive | ||
/// </summary> | ||
public bool IsCaseSensitive { get; } | ||
|
||
/// <summary> | ||
/// <see cref="System.StringComparer"/> based on <see cref="IsCaseSensitive"/> | ||
/// </summary> | ||
public StringComparer StringComparer { get; } | ||
|
||
/// <summary> | ||
/// Creates a new instance | ||
/// </summary> | ||
/// <param name="name">The name of the key</param> | ||
/// <param name="isCaseSensitive">Indicates wether or not the key is case sensitive or not</param> | ||
public Key(string name, bool isCaseSensitive) | ||
{ | ||
ArgumentNullException.ThrowIfNull(name, nameof(name)); | ||
|
||
Name = name; | ||
IsCaseSensitive = isCaseSensitive; | ||
StringComparer = StringComparer.FromComparison(IsCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override bool Equals(object? obj) => Equals(obj as Key); | ||
|
||
/// <inheritdoc/> | ||
public bool Equals(Key? other) | ||
{ | ||
if (other is null) | ||
{ | ||
return false; | ||
} | ||
|
||
// Optimization for a common success case. | ||
if (ReferenceEquals(this, other)) | ||
{ | ||
return true; | ||
} | ||
|
||
// If run-time types are not exactly the same, return false. | ||
if (GetType() != other.GetType()) | ||
{ | ||
return false; | ||
} | ||
|
||
// Return true if the fields match. | ||
// If either of the StringComparers return true, then they are equal (one might be case sensitive, and the other not - the non-case sensitive then overrules) | ||
return StringComparer.Equals(Name, other.Name) || other.StringComparer.Equals(Name, other.Name); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Name); // using case-insensitive HashCode here, in order to trigger an equality test (using .Equals) on hash collisions. | ||
|
||
/// <summary> | ||
/// Tests equality | ||
/// </summary> | ||
public static bool operator ==(Key lhs, Key rhs) | ||
{ | ||
if (lhs is null) | ||
{ | ||
if (rhs is null) | ||
{ | ||
return true; | ||
} | ||
|
||
// Only the left side is null. | ||
return false; | ||
} | ||
// Equals handles case of null on right side. | ||
return lhs.Equals(rhs); | ||
} | ||
|
||
/// <summary> | ||
/// Tests inequality | ||
/// </summary> | ||
public static bool operator !=(Key lhs, Key rhs) => !(lhs == rhs); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace Dnmh.Security.ApiKeyAuthentication.AuthenticationHandler; | ||
|
||
/// <summary> | ||
/// Extension methods for <see cref="ISet{Task}"/> with type <see cref="Key"/> | ||
/// </summary> | ||
public static class KeyHashSetExtensions | ||
{ | ||
/// <summary> | ||
/// Adds a new <see cref="Key"/> to the set | ||
/// </summary> | ||
public static bool Add(this ISet<Key> set, string value, bool isCaseSensitive = true) => | ||
set.Add(new Key(value, isCaseSensitive)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
using Dnmh.Security.ApiKeyAuthentication.AuthenticationHandler; | ||
using FluentAssertions; | ||
|
||
namespace Dnmh.Security.ApiKeyAuthentication.Tests.AuthenticationHandler; | ||
|
||
public class KeyTest | ||
{ | ||
[Fact] | ||
public void KeyEqualityTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("key", true); | ||
|
||
// Act & Assert | ||
key1.Should().Be(key2); | ||
} | ||
|
||
[Fact] | ||
public void KeyInequalityTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("KEY", true); | ||
|
||
// Act & Assert | ||
key1.Should().NotBe(key2); | ||
} | ||
|
||
[Fact] | ||
public void KeyCaseInsensitiveEqualityTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("KEY", false); | ||
|
||
// Act & Assert | ||
key1.Should().Be(key2); | ||
} | ||
|
||
[Fact] | ||
public void KeyCaseInsensitiveEqualityReverseTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", false); | ||
var key2 = new Key("KEY", true); | ||
|
||
// Act & Assert | ||
key1.Should().Be(key2); | ||
} | ||
|
||
[Fact] | ||
public void KeyHashSetAddSameKeyTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("key", true); | ||
|
||
// Act | ||
var set = new HashSet<Key>() { key1 }; | ||
var result = set.Add(key2); | ||
|
||
// Assert | ||
result.Should().BeFalse(); | ||
set.Should().ContainSingle(); | ||
} | ||
|
||
[Fact] | ||
public void KeyHashSetAddDifferentKeyTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("KEY", true); | ||
|
||
// Act | ||
var set = new HashSet<Key>() { key1 }; | ||
var result = set.Add(key2); | ||
|
||
// Assert | ||
result.Should().BeTrue(); | ||
set.Should().HaveCount(2); | ||
} | ||
|
||
[Fact] | ||
public void KeyHashSetAddSameCaseInsensitiveKeyTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", true); | ||
var key2 = new Key("KEY", false); | ||
|
||
// Act | ||
var set = new HashSet<Key>() { key1 }; | ||
var result = set.Add(key2); | ||
|
||
// Assert | ||
result.Should().BeFalse(); | ||
set.Should().ContainSingle(); | ||
} | ||
|
||
[Fact] | ||
public void KeyHashSetAddSameCaseInsensitiveKeyReverseTest() | ||
{ | ||
// Arrange | ||
var key1 = new Key("key", false); | ||
var key2 = new Key("KEY", true); | ||
|
||
// Act | ||
var set = new HashSet<Key>() { key1 }; | ||
var result = set.Add(key2); | ||
|
||
// Assert | ||
result.Should().BeFalse(); | ||
set.Should().ContainSingle(); | ||
} | ||
} |