Here is what I am trying to do:
I have implemented Form Authentication in ASP.NET MVC. I have IUser Interface which conforms to IPrincipal (System.Security.Principal). The custom IUser have additional properties and can be considered as a DTO. I need to use this user in different layers.
Currently my base controller checks whether the form is Authenticated and reconstructs the IUser as in Code1. I am passing this current User to Service Layer, which passes them to domain layer and then it gets to Events and Event Handlers( domain events).
All layers are Interface based and StructureMap is used as IoC.
My IoC is a separate class Library.
I am looking for a way to avaoiding pass user information to each and every method. I found that I could inject Custom Instance of a class as described in link http://structuremap.net/structuremap/InstanceExpression.htm#section11
I plan to create a Method
public void SetCurrentUser(IUser user)
{
// Something Similart to below ( Below code may be wrong)
//For<IUser>().TheDefault.IsThis(user);
}
and
have IUser in all class constructors which needs to know about current user
Questions
1) is this a right way to pass User Information to all layers and do you think it will work.
2) Is this safe, Can a user in one session be hijacked from another session?
Thank you,
Mar
Code(1)
string[] roles = userData.Split(',');
// Create a new Generic Principal Instance and assign to Current User
IUser _currentUser= new User
{
IsApplicationUser = Convert.ToBoolean(roles[0].ToString()),
Role = (UserRole)Enum.Parse(typeof(UserRole), roles[1].ToString()),
Id = new Guid(ticket.Name),
Email = roles[3].ToString(),
Name = roles[2].ToString(),
CompanyName = roles[4].ToString(),
DealerId = roles[5].ToString(),
LocationId = roles[6].ToString()
};
For<IUser>().HybridHttpOrThreadLocalScoped().Use( container => {
buildUserInstanceFromThreadCurrentPrincipal();
});
Related
Ok, I remember back in regular ASP.NET 4 (before .NET Core - 2015 ish) it was not this convoluted to add a user to a role. But now I found it to be very difficult.
Using Sqlite database, and all the scaffolding and account creation works great. Even imported my contact subscriber list and it seamlessly created the CRUD - awesome.
Now I only need to restrict this page to Admins only, which I did this and works -- no access. I am on the step to add an Admin role and add a user to it.
After reading and trying code from many sites I find myself here for some direction.
Also I see a role and role claim which is confusing..
Ref: How to create roles in ASP.NET Core and assign them to users?
This may work if I knew where to put this for it to find the proper references.
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _rolesManager;
Not sure where the ApplicationUser and ApplicationRole is coming from.
Assuming you have correctly configured Identity in your application with something similar to the following
builder.Services
.AddIdentity<IdentityUser, IdentityRole>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<WebApplication2Context>();
(in your case, IdentityUser is probably going to map to your AspNetUser class and IdentityRole would be your AspNetRole class, though I'm not sure where you got those classes or whether they properly inherit IdentityUser<T> and IdentityRole<T> - if they don't, you've gone down a dark and scary road...)
Once that's done (and you've applied all the EF Core migrations, etc. to get your database correctly built), you can do something like this to add a role and add a user to it
#page
#model IndexModel
#using Microsoft.AspNetCore.Identity
#inject UserManager<IdentityUser> _userManager
#inject RoleManager<IdentityRole> _roleManager
#{
ViewData["Title"] = "Home page";
await _roleManager.CreateAsync(new IdentityRole("Admin"));
await _userManager.CreateAsync(new IdentityUser("foobar") { Email = "foo#bar.com" });
var newUser = await _userManager.FindByNameAsync("foobar");
await _userManager.AddToRoleAsync(newUser, "Admin");
}
If you're not sure your existing AspNetXxx classes are correct or correctly map to the related Identity classes, I'd suggest you start over and use the default implementations as much as possible. You can read about Identity Model Customization in ASP.NET Core to learn about the model and how all the different tables work together.
I think you need to "link" your "MyPolicyName" to your MVC.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0#apply-policies-to-razor-pages
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AuthorizationPoliciesSample.Pages;
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Model : PageModel { }
...
Because of this "string-matching-magic" I would create a single-source-of-truth class.
public static class MyPolicyNames
{
public static string AtLeast21PolcyName = "AtLeast21PolicyName";
}
and refer to this const in both places. And eliminate future "where are the magic-strings" scavenger hunts.
=============
So you are using a Policy, but your "policy rule" is to then check a Role.
Should you be just using Role?
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0
I guess M$ says its "ok"
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0#policy-based-role-checks
In my project I have lot of endpoint views (APIViews, ViewSets). For all of them now I set permissions, some of them are default (e.g. AllowAny) and some are custom created:
permission_classes = (IsUserHaveSomePermission,)
Now I want to implement some flexible system, that will allow me to specify set of allowed endpoints for each user, for example:
On front-end I want to select some user and have a list of checkboxes that correspond to project's endpoints.
This is just an utopian solution, some details may be changed, but the main question is to how make something similar so that admins can basically dynamically change list of allowed endpoints/views for user?
thanks in advance
This solution can be implemented by storing if the user has permission to access the current request method and request path.
Create a new db model for storing the user, request method and request path. Lets say the name of the model is RequestPermission
Instead of the path you can store a constant representing the url so that you have the flexibility of editing the path later on. This constant can be the url name which is supported by django.
class RequestPermission(models.Model):
user = user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='request_permissions')
method = models.CharField(max_length=10)
path_name = models.CharField(max_length=200)
create a custom permission class:
class IsUserResuestAllowed(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
# you can choose how to get the path_name from the path
path_name = get_path_name(request.path)
return RequestPermission.objects.filter(user=user, method=request.method, path_name=path_name).exists()
Now you can use this class as the default permission class in rest framework settings or use it per view.
I'm developing two different applications, I will name them A and B.
A is an internet platform, where you can logon only if you have a valid user account.
B is an intranet platform, where users can authenticate via Active Directory. An administrator using application B should be able to create new user accounts for application A.
After the creation of a new user account, I want to be able to realize different functions, for example to send an e-mail to the registered mail address, so the new user can change the default password.
All the functionalities that I want to implement, can be done by the UserManager (see section "Use another app to add users" in the following link: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.1&tabs=visual-studio#disable-register-page).
Based on this I implemented the following code:
public class ControllerClass : Controller
{
private readonly HelperClass _helper;
private readonly UserManager<IdentityUser> _userManager;
public ControllerClass (UserManager<IdentityUser> userManager)
{
_userManager = userManager;
_helper= new HelperClass (userManager);
}
}
public class HelperClass
{
private readonly DbContext _db;
private readonly UserManager<IdentityUser> _userManager;
public HelperClass (UserManager<IdentityUser> userManager)
{
_db = new DbContext ();
_userManager = userManager;
}
private async Task<string> EnsureUser(string userName, string userPassword)
{
var user = await _userManager.FindByNameAsync(userName);
if (user == null)
{
user = new IdentityUser()
{
UserName = userName
};
await _userManager.CreateAsync(user, userPassword);
}
return user.Id;
}
internal async void CreateUser(UserVM uvm, int id)
{
var userId = await EnsureUser(uvm.userName, uvm.userPassword);
// TODO ...
}
}
Unfortunately I didn't manage to include the UserManager into my application B. I got the following error message: "An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[IdentityUser]' while attempting to activate 'ControllerClass '."
Do you have an idea, how I can add the UserManager to manage the users for another application?
Well, the specific error you're getting is simply because UserManager<TUser> is not registered in the service collection. In order to inject anything, you must first register it. In your actual user-facing app, that's being done by services.AddIdentity(). However, that does a lot more than just register UserManager<TUser>, so you shouldn't just run off and add the same command to your second app.
You could add a registration for it specifically:
services.AddScoped<UserManager<ApplicationUser>>();
However, it actually has a ton of dependencies, each of which would also need to be registered. If you look at the constructor, there's seven services not registered out of the box, many of which have their own dependency trees. Long and short, it's a pain. There's also the matter of separation of concerns, here. This would require adding in the whole data layer from the other app.
Your best bet is to simply expose an API on the Identity app (and lock it down, of course). That way, all the logic of working with users stays with the rest of that logic. The administration app, then, can call out to the API to add, update, delete, etc. users without having to have knowledge of how that's actually done.
Answering after 2 years. For future reader, You can use
services.AddIdentityCore<IdentityUser>();
which adds necessary services that are for user-management add/delete etc. without adding Login service.
to add EntityFramework you can create context and use like this
services.AddDbContext<ApplicationAuthDbContext>(options =>
{
// Configure the context to use postgresql.
options.UseNpgsql(config.GetConnectionString("AuthDbContext"))
.UseSnakeCaseNamingConvention();
});
services.AddIdentityCore<IdentityUser>()
.AddEntityFrameworkStores<ApplicationAuthDbContext>();
For more information
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.identityservicecollectionextensions.addidentitycore?view=aspnetcore-6.0
Given the following Controller
namespace MyNamespace.Api.Controllers
{
[Authorize]
public class AccountController : ODataController
{
private Entities db = new Entities();
// GET odata/Account
[Queryable]
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Read", Resource = "Account")]
public IQueryable<Account> GetAccount()
{
return db.Accounts();
}
...
}
}
I override the ClaimsAuthorizationManager.CheckAccess(...)
public class AuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
return Policies.Validate(resource, action);
}
}
This is useful only to the point where I can check whether or not the Current Principal in general can Read Account. However, if I'd want to check which accounts a certain user is allowed to Read, I am lost.
Let's say I have a Manager user who should be able to read all Accounts for which he is a manager for whereas a non-manager user should be able to read only their own account.
Is there a best practice for this or have you done something like this previously and give me a few hints to look for?
I do not use ClaimsPrincipalPermissionAttribute because I cannot pass any dynamic parameters to it like requested Account from your sample.
Have a look at the book "Pro APS.NET Web API Security" page 97. They suggest to invoke AuthorizationManager from your controller action implementation by code new IdentityConfiguration().ClaimsAuthorizationManager.CheckAccess(context), where context is constructed manually so you can pass Account requested (for example) as Resource to check it in your AuthorizationManager implementation.
Also have a look at pluralsight training "Introduction to Identity and Access Control in .NET 4.5". There are also some info about how to implement claim-based security in Web API.
Now I am in progress of implementing the security you are talking about and I am interesting in the subject too.
My case is: role Administrator is assigned by Country, every Administrator can see entities only related to the countries they have access to.
UPDATE: After several projects I forgot about Claims-based security as this is extremely difficult way to make security checks. Today I use decorator pattern where all the security checks are done. It appears to be very easy to implement security even in OData Controllers like this:
public IQueriable MyQuriableEntitySet
{
get{ return implementationWithoutSecurity.MyQuriableEntitySet.Where(e=>e.Country.Code = currentUser.AssignedTo.CountryCode || currentUser.IsSuperAdmin); }
}
http:www.site1.com/?sid=555
I want to be able to have the sid parameter and value persist whether a form is posted or a link is clicked.
If the user navigates to a view that implements paging, then the other parameters in the querystring should be added after the sid.
http:www.site1.com/?sid=555&page=3
How can I accomplish this in Asp.Net Mvc 2?
[Edit]
The url I mentioned on top would be the entry point of the application, so the sid will be included in the link.
Within the application links like:
<%= Html.ActionLink("Detail", "Detail", new { controller = "User",
id = item.UserId })%>
should go to:
http:www.site1.com/user/detail/3?sid=555
This question is different than what Dave mentions, as the querystring parameter is persisting throughout the site.
Firstly, I'd say if the value needs to be persisted throughout the session then you should store it in Session and check that its still valid on each action call. This can be done through a custom action attribute you add to the controller / actions required. If the value is required then when the value is checked you can re-drect to a login page or similar if not present or its expired.
Anyway, that said I thought I would have a crack at getting it working. My first thought would be to create a custom action filter attribute which took the value of the querstring and stored it in session in OnActionExecuting and then OnResultExecuted would add the key back to the querystring. But as QueryString in Request is a read-only collection you can't do it directly.
So, whats now available to you?
Option #1 - Add it to all calls to Html.ActionLink() manually
or ...
Option #2 - Override a version of ActionLink which automatically adds the value for you. This can be achived like so. I wouldn't recommend doing this though.
Start off with the custom attribute.
public class PersistQueryStringAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var sid = filterContext.RequestContext.HttpContext.Request.QueryString["sid"];
if (!string.IsNullOrEmpty(sid))
{
filterContext.RequestContext.HttpContext.Session["sid"] = sid;
}
base.OnActionExecuting(filterContext);
}
}
All this does is check the request querystring for the required key and if its available add it into the session.
Then you override ActionLink extention method to one of your own which adds the value in.
public static class HtmlHelperExtensions
{
public static MvcHtmlString ActionLink<TModel>(this HtmlHelper<TModel> helper, string text, string action, string controller, object routeValues)
{
var routeValueDictionary = new RouteValueDictionary(routeValues);
if (helper.ViewContext.RequestContext.HttpContext.Session["sid"] != null)
{
routeValueDictionary.Add("sid", helper.ViewContext.RequestContext.HttpContext.Session["sid"]);
}
return helper.ActionLink(text, action, controller, routeValueDictionary, null);
}
}
On each of the action which is going to be called apply the attribute (or apply it to the controller), eg:
[PersistQueryString]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
Note
As the query value gets put into session it will be applied for the life of the session. If you want to check that the value is there and the same each request you will need to do some checking in the attribute overridden method.
Finally
I've purely done this as a "can it be done" exercise. I would highly recommend against it.
Possible Duplicate:
How do you persist querystring values in asp.net mvc?
I agree with the accepted answer to the question linked above. Querystring parameters are not designed for data persistence. If a setting (i.e. sid=555) is intended to persist through a session, use Session state or your Model to save that data for use across requests.