In my HomeController I can perfectly access the ClaimsPrincipal, however using a static class it doesn't seem to be possible. How can I retrieve the ClaimsHelper?
public static class ClaimsHelper
{
public static List<Claim> GetCurrentUser()
{
return System.Security.Claims.ClaimsPrincipal.Current.Claims.ToList();
}
}
You can write an extension method for your need and use it in your actions
public static class HttpContextExtensions
{
public static ClaimsPrincipal GetClaimsPrincipal(this HttpContext httpContext)
{
var principal = httpContext.User as ClaimsPrincipal;
return principal;
}
}
and then use it in your actions:
[HttpGet]
public IActionResult Index ()
{
var user = HttpContext.GetClaimsPrincipal();
return Ok(user);
}
or you can use another way like this:
services.AddTransient<ClaimsPrincipal>(s =>
s.GetService<IHttpContextAccessor>().HttpContext.User);
Ref: Getting the ClaimsPrincipal in a logic layer in an aspnet core 1 application
or another way using DI just add IHttpContextAccessor then inject it to your helper class then register your helper class as a singleton service.
Though what the answer above does will work, I am not sure it's handling the use case you want. It seems you just need to have a variable in the static class that is the httpcontext. Then you can access it like you would anywhere else. Note, this is generally a poor pattern filled with landmines since you could be passing the context all over the place, but it does work. This should do it, but I have not yet tested.
public static class ClaimsHelper
{
public static List<Claim> GetCurrentUser(HttpContext context)
{
return context.User.Claims.ToList();
}
}
Inside a controller, you would call it like this:
public IActionResult Index ()
{
var ctx = HttpContext.Current;
var claims = ClaimsHelper.GetCurrentUser(ctx);
...
}
Related
In few places in legacy code (more than 100 controllers), we are running action from other controllers.
In .NET Framework it runs OK - ClaimsPrincipal in both controller's action have correct values, but in .NET Core, running SecondController.internalPut() from FirstController gives me NullReferenceException.
FirstController:
[EnableCors]
public class FirstController : BaseApiController
{
public FirstController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
[Route("/api/firstcontroller")]
public IActionResult Put([FromBody] MyDTO data)
{
var token = Identity.Token; // <--- correct value
var secondController = new SecondController(ContextFactory);
secondController.internalPut(something); <--- NullReferenceException
return Ok();
}
}
SecondController:
[EnableCors]
public class SecondController : BaseApiController
{
public SecondController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
public async Task<IActionResult> Put(Guid myGuid)
{
internalPut(something); // <-- OK
return Ok();
}
internal void internalPut(object something)
{
var token = Identity.Token; // <--- NullReferenceException when running from FirstController!!
}
}
And BaseApiController with TokenIdentity:
[ApiController]
[Route("/api/[controller]")]
[Route("/api/[controller]/[action]")]
public class BaseApiController : ControllerBase
{
protected readonly IMyContextFactory ContextFactory;
public BaseApiController(IMyContextFactory contextFactory)
{
ContextFactory = contextFactory;
}
public TokenIdentity Identity => User?.Identity as TokenIdentity;
}
public class TokenIdentity : GenericIdentity
{
public Guid Token { get; set; }
public string User { get; set; }
public TokenIdentity(Guid token) : base(token.ToString())
{
Token = token;
}
}
How is the easiest fix for this bug? I know that I can change BaseApiController implementation to get ClaimsPrincipal from IHttpContextAccessor, but this means that I need to update constructors for all > 100 controllers in code...
It is another way to always have ClaimsPrincipal when we are calling action from another controller?
What I recommend as the correct solution
I can't emphasise enough how much I recommend moving shared functionality into its own services, or perhaps look at using the Mediator Pattern (e.g. using the MediatR library) to decouple your controllers from their functionality a little. What I provide below is not a solution, but a band-aid.
What I recommend a QUICK FIX only
Why is this only a quick fix?: because this doesn't instantiate the correct action details and route parameters, so it could potentially cause you some hard-to-find bugs, weird behaviour, URLs maybe not generating correctly (if you use this), etc.
Why am I recommending it?: because I know that sometimes time is not on our side and that perhaps you need a quick fix to get this working while you work on a better solution.
Hacky quick fix
You could add the following method to your base controller class:
private TController CreateController<TController>() where TController: ControllerBase
{
var actionDescriptor = new ControllerActionDescriptor()
{
ControllerTypeInfo = typeof(TController).GetTypeInfo()
};
var controllerFactory = this.HttpContext.RequestServices.GetRequiredService<IControllerFactoryProvider>().CreateControllerFactory(actionDescriptor);
return controllerFactory(this.ControllerContext) as TController;
}
Then instead of var secondController = new SecondController(ContextFactory); you would write:
var secondController = CreateController<SecondController>();
I've started a new project using .Net 5 (my previous was .Net Framework 4.7). I'm writing a web API project and I want all my controllers/action responses to be of a certain type. This allows me to put some info I want included in every response, such as the current user info (and more stuff too).
My generic response looks like this (I've only left the relevant code):
public class MyResponse<T>
{
public T data { get; set; }
public User user { get; set; }
public MyResponse(T inputData)
{
data = inputData;
}
}
And I set the response on a controller's action this way:
public IActionResult Get()
{
var response = new MyResponse<string>("Hello");
return Ok(response);
}
So the idea is that the response always contains a "data" property with the actual data, and a bunch of other properties with metadata.
The problem is how to include information on the logged in user in .Net 5. In .Net 4.x you could just access HttpContext from anywhere, so you could just populate the User property. But this is not possible in .Net 5
I'm going crazy trying to understand how to achieve this in .Net 5.
The first thing I've tried is DI (which I'm new to, so I might not be understanding this properly).
The first thing I tried is to make my User class depend on IHttpContextAccessor as most documentation points to:
public class User : IIdentity
{
private readonly IHttpContextAccessor _httpContextAccessor;
public User(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
and register it this way on startup.cs:
services.AddHttpContextAccessor();
services.AddTransient<User>();
But that doesn't work well, since when I try to create my User class within MyResponse class:
var user = new User(); // This doesn't work, as the constructor requires one argument
So the constructor requires one argument so I can't create the class like that. I (believe) I would need to create the User from the DI container, but I don't have access to that on MyResponse class (or at least I couldn't really understand how to do it or if possible at all).
I could pass the HttpContext from the controller to MyResponse, but that seems plain wrong (plus, there might be other people writing controllers, so I think it's better if they don't explicitly need to pass that to the response, should be handled transparently)
My concrete questions:
Any thoughts of how can I get hold of the HttpContext within my custom response class?
Should I be looking for an alternative option (such as a Middleware or Filter) to generate my response?
Thank you very much.
You could use a factory along with dependency injection.
Create your user class:
using Microsoft.AspNetCore.Http;
using System.Security.Principal;
public class User : IIdentity
{
private IHttpContextAccessor HttpContextAccessor { get; }
public User(IHttpContextAccessor httpContextAccessor)
{
this.HttpContextAccessor = httpContextAccessor;
}
public string AuthenticationType => this.HttpContextAccessor.HttpContext.User.Identity.AuthenticationType;
public bool IsAuthenticated => this.HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated;
public string Name => this.HttpContextAccessor.HttpContext.User.Identity.Name;
}
Use DI to inject factories with the types you want:
services.AddHttpContextAccessor();
services.AddSingleton(a => GetResponse<string>(a));
services.AddSingleton(a => GetResponse<int>(a));
services.AddSingleton(a => GetResponse<decimal>(a));
Func<T, MyResponse<T>> GetResponse<T>(IServiceProvider serviceProvider)
{
var contextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
var user = new User(contextAccessor);
return (data) => new MyResponse<T>(user, data);
}
Then inject it where you want:
namespace WebAppFiles.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
private Func<int, MyResponse<int>> ResponseFactory { get; }
public MyController(Func<int, MyResponse<int>> responseFactory)
{
this.ResponseFactory = responseFactory;
}
[HttpGet]
public IActionResult Get([FromQuery] int value)
{
return Ok(this.ResponseFactory(value));
}
}
}
I am trying to add one more parameter to my constructors in my Asp.Net Core MVC application, but facing some difficulties to do so. Here is what my implementation looks like.
Login action:
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public IActionResult Login(LoginViewModel loginModel, string returnUrl = null)
{
returnUrl = string.IsNullOrWhiteSpace(returnUrl) ? ApiConstants.Dashboard : returnUrl;
ViewData["ReturnUrl"] = returnUrl;
if (!ModelState.IsValid) return View(loginModel);
var token = Service.Login(loginModel);
if (string.IsNullOrWhiteSpace(token)) return View(loginModel);
TempData["token"] = token;
AddCookie(token);
return RedirectToAction("Index", "Dashboard");
}
private void AddCookie(string token)
{
HttpContext.Response.Cookies.Append("token", token,new CookieOptions()
{
Expires = DateTimeOffset.Now.AddDays(-1)
});
}
Controller:
private readonly INozzleService _nozzleService;
public NozzleController(INozzleService nozzleService)
{
var token = HttpContext.Request.Cookies["token"];
_nozzleService = nozzleService;
}
Nozzle Service:
private static INozzleAdapter Adapter { get; set; }
public NozzleService(INozzleAdapter adapter)
{
Adapter = adapter;
}
Nozzle Adapter:
private readonly string _token;
public NozzleAdapter(string token)
{
_token = token;
}
Once I get the token in the adapter, I will be adding the token to the HttpClient header.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
ConfigureServices in Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
services.AddDistributedMemoryCache();
services.AddSession();
services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IAccountAdapter, AccountAdapter>();
services.AddTransient<INozzleService, NozzleService>();
services.AddTransient<INozzleAdapter, NozzleAdapter>();
services.AddMvc();
}
Can you please let me know what could be the best way to achieve this in Asp.Net core 2.0 MVC application? I have read a post saying that using multiple constructors is not a good idea in Asp.Net Core MVC application, so I don't want to use multiple constructors.
At the same time, I want to make sure all of my classes are unit testable with DI. What should be the best approach here?
Please let me know if anyone needs more information.
Update:
As per Shyju's solution, I was able to implement the cookie, however, I am still in a need to pass two parameters to one of my controllers.
private readonly IAccountService _service;
private readonly ITokenProvider _tokenProvider;
public AccountController(IAccountService service, ITokenProvider tokenProvider)
{
_service = service;
_tokenProvider = tokenProvider;
}
So that I can, use the method AddToken as below.
_tokenProvider.AddToken(token);
You may consider abstracting out the logic to get the token to a separate class and inject that as needed.
public interface ITokenProvider
{
/// <summary>
/// Gets the token
/// </summary>
/// <returns></returns>
string GetToken();
}
Now create an implementation of this, which will be reading the token from the cookie. Here is a simple implementation, which reads the token from the cookies collection
public class CookieTokenProvider : ITokenProvider
{
private readonly IHttpContextAccessor httpContextAccessor;
public CookieTokenProvider(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string GetToken()
{
if (httpContextAccessor.HttpContext.Request.Cookies
.TryGetValue("token", out string tokenValue))
{
return tokenValue;
}
return null;
}
}
Now, you can inject the ITokenProvider implementation to anyplace you want and call the GetToken method to get the token value. For example, you may inject this to the NozzleAdapter class constructor.
private readonly ITokenProvider tokenProvider;
public NozzleAdapter(ITokenProvider tokenProvider)
{
tokenProvider=tokenProvider;
}
public string SomeOtherMethod()
{
var token = this.tokenProvider.GetToken();
//Do null check and use it
}
Make sure you register this in the ConfigureServices method in Startup class
services.AddTransient<ITokenProvider, CookieTokenProvider>();
Regarding your comment about getting the token and persisting it, it is up to you where you want to do it. You can do that in the CookieTokenProvider implementation. Read the value and store it somewhere ( a local db, in memory cache etc) and get it from there if exists (the next time)
Now, for your unit tests you can create a MockTokenProvider which does not use HttpContext, but simply return a mock value for your testing,
public class MockTokenProvider : ITokenProvider
{
public string GetToken() => "FakeToken";
}
As I am working on Asp.Net core Authorization part, I needed a new property in AuthorizeAttribute which I want to utilize as a extra permission value. So, I have extended the AuthorizeAttribute in my own custom Authorize attribute. See below:
public class RoleAuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
{
public string Permission { get; private set; }
public RoleAuthorizeAttribute(string policy, string permission) : base(policy)
{
this.Permission = permission;
}
}
Then, I've created an AuthorizationHandler to check for the requirement as below:
public class RolePermissionAccessRequirement : AuthorizationHandler<RolePermissionDb>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolePermissionDb requirement)
{
// check here..
context.Succeed(requirement);
return Task.FromResult(0);
}
}
All respective service collection mapping I have already done, just omitted here.
Now, I want my attribute to use like this on controller action level:
[RoleAuthorize("DefaultPolicy", "CustomPermission")]
public IActionResult List()
{
}
Would anybody suggest me how would I access the permission property value given on the top of Action method in the handler RolePermissionAccessRequirement ??
I want to perform some sort of access rule based on custom permission value given in the Authorize attribute on top of Action method.
Thanks in advance!
To parametrize a custom Authorize attribute, create an authorization filter implementing IAsyncAuthorizationFilter. Then wrap the filter in a TypeFilterAttribute-derived attribute. This attribute can accept parameters and pass it to the authorization filter's constructor.
Usage example:
[AuthorizePermission(Permission.Foo, Permission.Bar)]
public IActionResult Index()
{
return View();
}
Implementation:
public class AuthorizePermissionAttribute : TypeFilterAttribute
{
public AuthorizePermissionAttribute(params Permission[] permissions)
: base(typeof(PermissionFilter))
{
Arguments = new[] { new PermissionRequirement(permissions) };
Order = Int32.MinValue;
}
}
public class PermissionFilter : Attribute, IAsyncAuthorizationFilter
{
private readonly IAuthorizationService _authService;
private readonly PermissionRequirement _requirement;
public PermissionFilter(
IAuthorizationService authService,
PermissionRequirement requirement)
{
//you can inject dependencies via DI
_authService = authService;
//the requirement contains permissions you set in attribute above
//for example: Permission.Foo, Permission.Bar
_requirement = requirement;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool ok = await _authService.AuthorizeAsync(
context.HttpContext.User, null, _requirement);
if (!ok) context.Result = new ChallengeResult();
}
}
In addition, register a PermissionHandler in DI to handle PermissionRequirement with permission list:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
Look at this this GitHub project for a complete example.
I'm using ThinkTecture's resource based authorization in my WebApi.
I'm trying to test one of my controller that I needed to check the access inside the function. But now, I can't test the function anymore since, I can't mock an extension method and since it's a nuget method, I can't modify the class to inject another value.
My controller look like this:
public class AlbumController : ApiController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await Request.CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And the ResourceAuthorizationManager is setted into the startup like this:
app.UseResourceAuthorization(new ChinookAuthorization());
Source code of the ThinkTecture project is here.
Thank you for your help
The ResourceAuthorizationAttribute uses Reqest.CheckAccess so I don't think it is a good solution to abstract away the implementation and then injecting it into the controller since in theory, the ResourceAuthorizationAttribute and the created service could use different implementations of the CheckAccess method.
I took a simpler approach by creating a BaseController
public class BaseController : ApiController
{
public virtual Task<bool> CheckAccessAsync(string action, params string[] resources)
{
return Request.CheckAccessAsync(action, resources);
}
}
and making CheckAccessAsync virtual so I can mock it (by for example Moq).
then from my controller
public class AlbumController : BaseController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
Unit testing the controller then is as easy as:
[TestClass]
public class TestClass
{
Mock<AlbumController> mockedTarget
AlbumController target
[TestInitialize]
public void Init()
{
mockedTarget = new Mock<AlbumController>();
target = mockedTarget.Object;
}
[Test]
public void Test()
{
mockedTarget.Setup(x => x.CheckAccessAsync(It.IsAny<string>(),
It.IsAny<string[]>()))
.Returns(Task.FromResult(true));
var result = target.Get(1);
// Assert
}
}
You could always wrap this static call into some abstraction of yours:
public interface IAuthorizationService
{
Task<bool> CheckAccessAsync(string view, string album, string id);
}
and then have some implementation that will delegate the call to the static extension method. But now since you will be working with the IAuthorizationService you can freely mock the CheckAccessAsync method in your unit tests.
As far as testing the implementation of this abstraction is concerned, you probably don't need it as it only acts as a bridge to the ThinkTecture's classes which should already be pretty well tested.
I finally solved my problem.
The real problem was that the CheckAccess method was an extension.
(for my answer, every class will refer to the sample that can be find here)
To stop using the extension method, I added these methods into my chinookAuthorization
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, string action, params string[] resources)
{
var ctx = new ResourceAuthorizationContext(user ?? Principal.Anonymous, action, resources);
return CheckAccessAsync(ctx);
}
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
user ?? Principal.Anonymous,
actions,
resources);
return CheckAccessAsync(authorizationContext);
}
Then I changed my controller to have an instance of the chinookAuthorization
public class AlbumController : ApiController
{
protected readonly chinookAuthorization chinookAuth;
public BaseApiController(chinookAuthorization chinookAuth)
{
if (chinookAuth == null)
throw new ArgumentNullException("chinookAuth");
this.chinookAuth = chinookAuth;
}
public async Task<IHttpActionResult> Get(int id)
{
if (!(await chinookAuth.CheckAccessAsync((ClaimsPrincipal)RequestContext.Principal, ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And I'm still declaring my ChinookAuthorization into my owin startup, to keep using the same pattern for my attribute check access call.
So now, I just have to mock the chinookAuthorization, mock the response of the call to return true, and that's it!