diff --git a/README.md b/README.md index 6d6ca52..51f7499 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ If a claim with the given name is found the modelbinder will try to convert the - `string` - `Guid` - `Enum` +- `int` **Note:** Which claims exist in the User object is dependent on your authentication middleware and out of the scope of this repository. For example you can extend the `AuthenticationHandler` like described in [the official docs](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-7.0#authentication-handler) and add custom claims to the user. diff --git a/src/DroidSolutions.Oss.AuthClaimBinder/ClaimModelBinder.cs b/src/DroidSolutions.Oss.AuthClaimBinder/ClaimModelBinder.cs index 2a38c8a..e054be5 100644 --- a/src/DroidSolutions.Oss.AuthClaimBinder/ClaimModelBinder.cs +++ b/src/DroidSolutions.Oss.AuthClaimBinder/ClaimModelBinder.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Security.Claims; using DroidSolutions.Oss.AuthClaimBinder.Exceptions; @@ -72,6 +73,24 @@ public Task BindModelAsync(ModelBindingContext bindingContext) bindingContext.Result = ModelBindingResult.Success(value); } + else if (bindingContext.ModelType == typeof(int)) + { + try + { + bindingContext.Result = ModelBindingResult.Success(int.Parse(claim.Value, CultureInfo.InvariantCulture)); + } + catch (Exception ex) + { + bindingContext.Result = ModelBindingResult.Failed(); + _logger.LogError(ex, "The claim {FieldName} could not be parsed to an Int32!", bindingContext.FieldName); + + throw new ClaimParsingException( + $"The claim {bindingContext.FieldName} could not be parsed to an Int32!", + ex, + bindingContext.FieldName, + bindingContext.ModelType); + } + } else { bindingContext.Result = ModelBindingResult.Success(claim.Value); diff --git a/test/DroidSolutions.Oss.AuthClaimBinderTest/ClaimModelBinderTests.cs b/test/DroidSolutions.Oss.AuthClaimBinderTest/ClaimModelBinderTests.cs index 6db8eb6..1497ef2 100644 --- a/test/DroidSolutions.Oss.AuthClaimBinderTest/ClaimModelBinderTests.cs +++ b/test/DroidSolutions.Oss.AuthClaimBinderTest/ClaimModelBinderTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Security.Claims; using System.Threading.Tasks; @@ -220,4 +221,47 @@ public async Task BindingModelAsync_ShouldThrowInvalidOperationException_WhenEnu await Assert.ThrowsAsync(() => claimModelBinder.BindModelAsync(bindingContext.Object)); } + + [Fact] + public async Task BindingModelAsync_ShouldBeAbleToParseInts() + { + int num = 42; + Claim[] claims = [new Claim("someId", num.ToString(CultureInfo.InvariantCulture))]; + ClaimsIdentity identity = new (claims, "Scheme"); + ClaimsPrincipal principal = new(identity); + + Mock httpContext = new(); + httpContext.Setup(hc => hc.User).Returns(principal); + + Mock bindingContext = new() { CallBase = true }; + bindingContext.Setup(bc => bc.HttpContext).Returns(httpContext.Object); + bindingContext.Setup(bc => bc.FieldName).Returns("someId"); + bindingContext.Setup(bc => bc.ModelType).Returns(typeof(int)); + + ClaimModelBinder claimModelBinder = new(_logMock.Object, null); + + await claimModelBinder.BindModelAsync(bindingContext.Object); + + Assert.Equal(num, bindingContext.Object.Result.Model); + } + + [Fact] + public async Task BindingModelAsync_ShouldThrowInvalidOperationException_WhenIntIsNotParsable() + { + Claim[] claims = [new Claim("someId", "42,5d")]; + ClaimsIdentity identity = new(claims, "Scheme"); + ClaimsPrincipal principal = new(identity); + + Mock httpContext = new(); + httpContext.Setup(hc => hc.User).Returns(principal); + + Mock bindingContext = new() { CallBase = true }; + bindingContext.Setup(bc => bc.HttpContext).Returns(httpContext.Object); + bindingContext.Setup(bc => bc.FieldName).Returns("someId"); + bindingContext.Setup(bc => bc.ModelType).Returns(typeof(int)); + + ClaimModelBinder claimModelBinder = new(_logMock.Object, null); + + await Assert.ThrowsAsync(() => claimModelBinder.BindModelAsync(bindingContext.Object)); + } }