Skip to content

Commit

Permalink
Merge pull request #57 from VahidFarahmandian/util
Browse files Browse the repository at this point in the history
JsonUtilities added.
  • Loading branch information
VahidFarahmandian committed Aug 5, 2023
2 parents 6b4d7a4 + 4e03906 commit 89539d8
Show file tree
Hide file tree
Showing 14 changed files with 439 additions and 0 deletions.
1 change: 1 addition & 0 deletions 01-Core/Jinget.Core/Jinget.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="System.CodeDom" Version="6.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1" />
</ItemGroup>

<ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions 01-Core/Jinget.Core/Utilities/Json/IgnorePropertiesResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Reflection;

namespace Jinget.Core.Utilities.Json
{
/// <summary>
/// ignore the given properties, while serializing an object
/// </summary>
public class IgnorePropertiesResolver : DefaultContractResolver
{
private readonly HashSet<string> _ignoreProps;
public IgnorePropertiesResolver(IEnumerable<string> propNamesToIgnore) => _ignoreProps = new HashSet<string>(propNamesToIgnore);

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (_ignoreProps.Contains(property.PropertyName))
{
property.ShouldSerialize = _ => false;
}
return property;
}
}
}
33 changes: 33 additions & 0 deletions 01-Core/Jinget.Core/Utilities/Json/JsonUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Newtonsoft.Json.Linq;

namespace Jinget.Core.Utilities.Json
{
public class JsonUtility
{
/// <summary>
/// check if given string is a valid json string or not.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static bool IsValid(string input)
{
if (string.IsNullOrWhiteSpace(input)) { return false; }
input = input.Trim();
if ((input.StartsWith("{") && input.EndsWith("}")) || //For object
(input.StartsWith("[") && input.EndsWith("]"))) //For array
{
try
{
var tmpObj = JToken.Parse(input);
return true;
}
catch
{
return false;
}
}

return false;
}
}
}
31 changes: 31 additions & 0 deletions 01-Core/Jinget.Core/Utilities/Json/NonPublicSetterResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Jinget.Core.Utilities.Json
{
/// <summary>
/// Serialize even properties with non public setter
/// </summary>
public class NonPublicSetterResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);

if (!prop.Writable)
{
var property = member as PropertyInfo;
if (property != null)
{
var hasNonPublicSetter = property.GetSetMethod(true) != null;
prop.Writable = hasNonPublicSetter;
}
}

return prop;
}
}
}
87 changes: 87 additions & 0 deletions 01-Core/Jinget.Core/Utilities/JwtUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;

namespace Jinget.Core.Utilities
{
public class JwtUtility
{
public static JwtSecurityToken Read(string token, string scheme = "Bearer")
{
try
{
if (token.StartsWith($"{scheme} ", StringComparison.InvariantCultureIgnoreCase))
token = token[7..];
return new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
}
catch
{
return null;
}
}

/// <summary>
/// Check if token is valid or not. This method only checks for the validity of lifetime, audience and issuer
/// </summary>
/// <param name="validAudiences">Expected list of audiences. It is expected that token was issued for one of these audiences</param>
/// <param name="validissuer">Expected issuer. It is expected that token was issued for this issuer</param>
/// <param name="minuteOffset">The given token is valid if it is valid for the next <paramref name="minuteOffset" /> minute(s)</param>
public static async Task<bool> IsValidAsync(string token,
IEnumerable<string> validAudiences = null,
string validissuer = null,
int minuteOffset = 5)
{
var result = await new JwtSecurityTokenHandler().ValidateTokenAsync(token, new TokenValidationParameters()
{
ClockSkew = TimeSpan.FromMinutes(minuteOffset),

ValidateLifetime = true,
LifetimeValidator = (DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) =>
{
var clonedParameters = validationParameters.Clone();
clonedParameters.LifetimeValidator = null;
Validators.ValidateLifetime(notBefore, expires, securityToken, clonedParameters);
return true;
},

ValidateAudience = true,
ValidAudiences = validAudiences,
AudienceValidator = (IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) =>
{
if (validAudiences != null && validAudiences.Any())
{
var clonedParameters = validationParameters.Clone();
clonedParameters.AudienceValidator = null;
Validators.ValidateAudience(audiences, securityToken, clonedParameters);
}
return true;
},

ValidateIssuer = true,
ValidIssuer = validissuer,
IssuerValidator = (string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) =>
{
string resultIssuer = "";
if (!string.IsNullOrWhiteSpace(validissuer))
{
var clonedParameters = validationParameters.Clone();
clonedParameters.IssuerValidator = null;
resultIssuer = Validators.ValidateIssuer(issuer, securityToken, clonedParameters);
}
return resultIssuer;
},

ValidateIssuerSigningKey = false,
SignatureValidator = (string token, TokenValidationParameters parameters) => new JwtSecurityToken(token)
});
if (result.Exception != null)
throw result.Exception;

return result.IsValid;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections.Generic;

namespace Jinget.Core.Utilities.Parser.SqlServer
{
public static class DatabaseParserUtility
{
public static Dictionary<string, string> ParseConnectionString(string connectionString)
{
string[] parts = connectionString.Split(";", System.StringSplitOptions.RemoveEmptyEntries);
var keyValuePairs = new Dictionary<string, string>();
foreach (var item in parts)
{
var keyValues = item.Split('=', System.StringSplitOptions.RemoveEmptyEntries);
if (keyValues.Length == 2)
keyValuePairs.Add(keyValues[0].Trim(), keyValues[1].Trim());
}
return keyValuePairs;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Jinget.Core.Utilities.Parser.SqlServer;
using System.Collections.Generic;

namespace Jinget.Core.Utilities.Parser.DatabaseParser.SqlServer
{
public static class SqlServerParserUtility
{
public static Dictionary<string, string> ParseConnectionString(string connectionString) => DatabaseParserUtility.ParseConnectionString(connectionString);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Jinget.Core.Utilities.Json;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

namespace Jinget.Core.Tests.Utilities.Json
{
[TestClass()]
public class IgnorePropertiesResolverTests
{
[TestMethod()]
public void should_ignore_given_properties_while_serialization()
{
string expected = "{\"Property1\":1,\"Property4\":false,\"InnerSingularProperty\":null,\"InnerProperty\":null,\"InnerListProperty\":null}";

var result = JsonConvert.SerializeObject(new _BaseData.TestClass
{
Property1 = 1
},
new JsonSerializerSettings()
{
ContractResolver = new IgnorePropertiesResolver(new[] { nameof(_BaseData.TestClass.Property2), nameof(_BaseData.TestClass.Property3) })
});

Assert.AreEqual(expected, result);
}
}
}
41 changes: 41 additions & 0 deletions Tests/Jinget.Core.Tests/Utilities/Json/JsonUtilityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Jinget.Core.Utilities.Json;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Jinget.Core.Tests.Utilities.Parser.DatabaseParser
{
[TestClass]
public class JsonUtilityTests
{
[TestMethod()]
public void should_return_true_for_valid_json_object_string()
{
string sampleJson = "{ id: 1, name: \"Leanne Graham\", username: \"Bret\", address: { street: \"Kulas Light\", city: \"Gwenborough\", geo: { lat: \"-37.3159\", lng: \"81.1496\" } }}";
var result = JsonUtility.IsValid(sampleJson);
Assert.IsTrue(result);
}

[TestMethod()]
public void should_return_true_for_valid_json_array_string()
{
string sampleJson = "[ { id: 1, name: \"Leanne Graham\", address: { city: \"Gwenborough\" }},{ id: 2, name: \"Vahid Farahmandian\", address: { city: \"Urmia\" }} ]";
var result = JsonUtility.IsValid(sampleJson);
Assert.IsTrue(result);
}

[TestMethod()]
public void should_return_false_for_invalid_json()
{
string invalidJsonString = "InvalidJsonString";
var result = JsonUtility.IsValid(invalidJsonString);
Assert.IsFalse(result);

string emptyJsonString = "";
result = JsonUtility.IsValid(emptyJsonString);
Assert.IsFalse(result);

string nullJsonString = "";
result = JsonUtility.IsValid(nullJsonString);
Assert.IsFalse(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Jinget.Core.Utilities.Json;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

namespace Jinget.Core.Tests.Utilities.Json
{
[TestClass()]
public class NonPublicSetterResolverTests
{
[TestMethod()]
public void should_ignore_given_properties_while_serialization()
{
string expected = "{\"Id\":1,\"Name\":\"Vahid\"}";

var result = JsonConvert.SerializeObject(new _BaseData.ClassWithNonPublicSetterProps("Vahid")
{
Id = 1
},
new JsonSerializerSettings()
{
ContractResolver = new NonPublicSetterResolver()
});

Assert.AreEqual(expected, result);
}
}
}
48 changes: 48 additions & 0 deletions Tests/Jinget.Core.Tests/Utilities/JwtUtilityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Jinget.Core.Utilities.Tests
{
[TestClass()]
public class JwtUtilityTests
{
[TestMethod()]
public void should_return_token_parts()
{
string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiSmluZ2V0IiwiaWF0IjoxNTE2MjM5MDIyfQ.Ushn140BB6h_G4rEnZuM2VWSKmatFc4DVrvJGWlRRfE";
var result = JwtUtility.Read(token);
Assert.IsNotNull(result.Subject);
Assert.IsNotNull(result.Issuer);
Assert.IsNotNull(result.IssuedAt);
}

[TestMethod()]
public async Task should_validate_token_with_lifetime_sigingkey()
{
string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiSmluZ2V0IiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjIwMTYyMzkwMjIsImF1ZCI6IkppbmdldC5UZXN0In0.e-GVmjCsuP6sv7csybQZbVp5HenQ1UT5AhzafYSlMFU";
var result = await JwtUtility.IsValidAsync(token);
Assert.IsTrue(result);
}

[TestMethod()]
public async Task should_validate_token_with_lifetime_sigingkey_audience_issuer()
{
string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaXNzIjoiSmluZ2V0IiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjIwMTYyMzkwMjIsImF1ZCI6IkppbmdldC5UZXN0In0.e-GVmjCsuP6sv7csybQZbVp5HenQ1UT5AhzafYSlMFU";
string validIssuer = "Jinget";
IEnumerable<string> validAudiences = new string[] { "Jinget.Test" };

var result = await JwtUtility.IsValidAsync(token, validAudiences, validIssuer);
Assert.IsTrue(result);
}

[TestMethod()]
[ExpectedException(typeof(SecurityTokenMalformedException))]
public async Task should_return_false_for_invalid_tokenAsync()
{
string token = "InvalidJwtToken";
var result = await JwtUtility.IsValidAsync(token);
}
}
}
Loading

0 comments on commit 89539d8

Please sign in to comment.