We are currently working on a smaller ASP.NET MVC 5 application using ASP.NET Identity. It allows us to maintain different projects and their tasks. We recently implemented basic authentication so we are able to register a user with our site and login with them.
We want to be able to manage access rights on project basis so we can say for every single user that he has read, write, admin or no permissions for a specified project.
My first thought was that we can create a simple new table in our database which stores the user rights. But I feel that there might be a built-in way to achieve this with ASP.NET Identity.
So my question really is, which path we should follow - manually building a new table to administer the rights or use something built-in provided by ASP.NET Identity.
use something built-in provided by ASP.NET Identity
The only things you could use there are claims or roles and both are not built for what you want IMO.
So I would go with your own table which links the project to a user, e.g.:
public class UserProjectRights
{
[Key]
public ApplicationUser User { get; set; }
[Key]
public Project Project { get; set; }
public AccessRight Right { get; set; }
}
Then whenever you do some actions where a specific right is required you need to check for that. There are several ways how you could do that. In my app I created "access right check extensions" like the following (I have defined a common interface for all "access right entities" to "reuse" that method):
public static bool? CanView(this ApplicationUser user, Project project)
{
var userRight = project.Rights.FirstOrDefault(r => r.User == user);
return userRight == null ? (bool?)null : userRight.Right.HasFlag(AccessRight.View);
}
assuming AccessRight is an enum like:
[Flags]
public enum AccessRight
{
View,
Edit,
Admin
}
Then you can do something like the following in your logic:
if (user.CanView(project) == true)
{
// show project
}
I used bool? so I can implement different "default behaviour" as I know if null is returned there is no right defined.
Related
I am looking for a solution/suggestion that helps me creating permission based access to web api endpoints/controller actions.
Role based access is not suitable becuase I don't have fixed rules that I could use in code like Role("Admin") oder Role("Controller").
Claim based permissions is also not feasable because each user/client can have different permissions on each business object/entity (e.g. Read/Write-access to own tickets and read access to all ticket of his/her company or if its a technician of my company full access to all tickets of all customers. So each user would have 10s or even hundrets of claims which I would have to evaluate at each access of my API.
It is some kind of multi tenancy in just on database and the tenants are our customers with some kind of "master tenant" that has access to all of the tenant data.
I think that something like Visual Guard would satisfy my needs but it is pretty expensive and they don't support net core for now and their documentation seems pretty outdated.
I don't need a usable solution at once but some hints and tricks how I could achieve that would very much be apprieciated because I am looking and searching for some time now.
Details on "database permissions":
What I mean is in my frontend (Winforms app) I want to establish a security system where I can create and assign roles to users and in those roles is defined which actions a user can execute and which CRUD operations he/she can do on specific business objects. Each role can have n users and each role can have n permissions. Each permission on itself declares for exmaple Create:false, Read:true, Write:true and Delete:false. If a permission for a specific business object is not found CRUDs on that BO is denied totally.
So whenever an action in my API is called I have to check if that user and his/her rule allows him to do that specific action based on rules and permissions in my database.
Details an application structure:
Frontend will be a Winforms app which calls the API in the background by OData. I don't want to rely solely on security in the Winforms app because the API will be accessible from the internet and I can't be sure if a user would not try to access the api with his credentials just to see what is possblie without the "frontend filter". So the permissions lie in the API and if a user tries to access s.t. in the frontend app the app itself "asks" the API if that is possible.
Later on I want to create mobile clients that also use the Odata Web API.
The relevant API in asp.net core are:
IAuthorizationService
AuthorizationPolicy
IAuhtorizationRequirement
IAuthorizationHandler
The authorization pattern you are looking for is called Resource-based authorization
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-2.2
Basically, you can define AuthorizationPolicy, and apply it to a instance of a resource:
var ticket = _ticketRepository.Find(ticketID);
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, ticket, "EditTicketPolicy");
In the authorization handler, you can check if the user is the owner of the resource.
public class ResourceOwnerRequirement : IAuthorizationRequirement
{
}
public class ResourceOwnerHandler
: AuthorizationHandler<ResourceOwnerRequirement, MyBusinessObject>
//: AuthorizationHandler<ResourceOwnerRequirement> use this overload to handle all types of resources...
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
ResourceOwnerRequirement requirement,
MyBusinessObject resource)
{
int createdByUserId = resource.CreatedBy;
Claim userIdClaim = ((ClaimsIdentity)context.User.Identity).FindFirst("UserId");
if (int.TryParse(userIdClaim.Value, out int userId)
&& createdByUserId == userId)
{
context.Succeed(requirement);
}
}
}
//admin can do anything
public class AdminRequirementHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.User.Claims.Any(c => c.Type == "Role" && c.Value == "Administator"))
{
while (context.PendingRequirements.Any())
{
context.Succeed(context.PendingRequirements.First());
}
}
return Task.CompletedTask;
}
}
BTW, this still can be called claims or role based authorization. Users with specific role can edit their own tickets, but users with admin role also other tickets. The difference is that you apply authorization to a resource, not just action
EDIT:
I am currently working on multitenant saas webapp using aspboilerplate and would like to implement the permission management but I got little bit of confusion.
Firstly, what's the difference between MultitenancySides.Host and MultitenancySides.Tenant which I have found here....
public override void SetPermissions(IPermissionDefinitionContext context)
{
context.CreatePermission(PermissionNames.Pages_Users, L("Users"));
context.CreatePermission(PermissionNames.Pages_Roles, L("Roles"));
context.CreatePermission(PermissionNames.Pages_Tenants, L("Tenants"), multiTenancySides: MultiTenancySides.Host);
context.CreatePermission(PermissionNames.Pages_Events, L("Events"), multiTenancySides: MultiTenancySides.Tenant);
}
Secondly, how [AbpAuthorize] differs to
[AbpAuthorize(PermissionNames.Pages_Tenants)]
I found those in event service ( first link and second link) ,
[AbpAuthorize]
public class EventAppService : EventCloudAppServiceBase, IEventAppService
{
private readonly IEventManager _eventManager;
private readonly IRepository<Event, Guid> _eventRepository;
and in TenantService
[AbpAuthorize(PermissionNames.Pages_Tenants)]
public class TenantAppService : AsyncCrudAppService<Tenant, TenantDto, int, PagedResultRequestDto, CreateTenantDto, TenantDto>, ITenantAppService
{
private readonly TenantManager _tenantManager;
Here is what I want and expect to implement permissions in my Multitenant Saas (Core + Angualr SPA) ...
There will be different features ( I say modules) including Event (CRUD), Library(CRUD), Exam(CRUD), Result(CRUD), Attendance(CRUD) and I would like to have full access (CRUD) permission to tenant superadmin (by default assigned when tenant is created) and then superadmin can create role (Assigned module and permission (CRUD) to specific module).
To make it simple, if system has Attendance module/feature then by default a tenant's Superadmin will have full CRUD access whereas if Staff role is created and restricted to CR but not allowed to UD.
I have gone through this tutorial but unable to sort it out.
Thank you.
Firstly, what's the difference between MultiTenancySides.Host and MultitenancySides.Tenant?
MultiTenancySides.Host → can only be assigned to Host users (user.TenantId == null).
MultiTenancySides.Tenant → can only be assigned to Tenant users (user.TenantId != null).
Secondly, how [AbpAuthorize] differs from [AbpAuthorize(PermissionNames.Pages_Tenants)]?
[AbpAuthorize] → user is logged in.
[AbpAuthorize(PermissionNames.Pages_Tenants)] → user is logged in and has that permission.
For reasons I would rather not discuss, I need to create a custom authentication system for my app. I was just reviewing the system and am having some doubts if my solution is thread safe. My goal was to create a solution that would allow my app to authenticate a user one time and that users authentication info would be shared by all master pages, pages, classes, user controls, etc that are used. (But not share the same info between users)
Here is my setup:
PageHttpModule.cs - this is added to the web.config as a httpModule.
public class PageHttpModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
}
public void OnAuthenticateRequest(Object s, EventArgs e)
{
CurrentUser.Initialize();
}
public void Dispose() { }
}
CurrentUser.cs
public static class CurrentUser
{
public static bool IsAuthenticated { get; private set; }
public static string Email {get; set;}
public static string RealName {get; set;
public static string UserId {get; set;}
public static void Initialize()
{
CurrentUser.AuthenticateUser();
}
Note: this is a scaled down version of my authentication code.
public static void AuthenticateUser()
{
UserAuthentication user = new UserAuthentication();
user.AuthenticateUser();
if (user.IsAuthenticated)
{
CurrentUser.IsAuthenticated = true;
CurrentUser.UserId = user.UserId;
CurrentUser.Email = user.Email;
CurrentUser.RealName = user.RealName;
}
}
}
UserAuthentication.cs
public class UserAuthentication
{
public string Email { get; set; }
public string RealName { get; set; }
public string UserId { get; set; }
public bool IsAuthenticated { get; private set; }
public UserAuthentication()
{
IsAuthenticated = false;
Email = String.Empty;
RealName = String.Empty;
UserId = String.Empty;
}
public void AuthenticateUser()
{
//do some logic here.. if the user is ok then
IsAuthenticated = true
Email = address from db
UserId = userid from db;
Realname = name from db;
}
}
I have tested between 3 different browsers and it seems to work fine, but I am still learning and don't want to make a huge mistake.
If my logic is totally wrong, then how should I do it so I dont have to put user lookups on every page directly?
No, this is not thread-safe. For instances of the application living in separate processes or AppDomains, this will be just fine. But if your ASP.NET server is going to serve multiple requests at once using threading, you are going to have some very bad side effects if two people try to use the application at the same time.
In the Init method, the HttpApplication parameter is described as:
An HttpApplication that provides access to the methods, properties, and events common to all application objects within an ASP.NET application
The key here is that there is one PageHttpModule for the lifetime of the app, and all static objects that exist in the lifetime of the app will share those variables.
BUT... the lifetime of CurrentUser is only within the scope of the OnAuthenticateRequest event, unless some other reference keeps the object alive. If it were a PageHttpModule member-level variable, you'd have issues that you would have noticed immediately. In your situation, however, you'll work fine so long as you don't get more than one simultaneously-processed OnAuthenticateRequest call.
The answer to your question is no, you're not guaranteed to be thread-safe. If two authentication requests come in simultaneously, you're not guaranteed to have one event complete before the other begins, in which case the second user can appear authenticated, when it's really the first user that was logged on.
Update
I think part of the problem is coming from a misunderstanding of AuthenticateRequest... By the time this event is called, the user has already been authenticated by either Windows or Forms authentication... you're just getting notified that it's happened. In fact, the property User.Identity.IsAuthenticated has already been set (I believe this event fires even if the user fails authentication, but I won't swear to that without double-checking).
If I understand what you are after, you're really trying to write your own custom membership provider. If you take this approach, you will have all the benefits of the built-in authentication... all of the standard properties related to authentication will be set and accessible, and will be isolated to a user's session in the manner you want.
Writing a custom provider is not a small feat, but it is doable, and you should be able to reuse a lot of the logic and code you're currently using for your classes.
Trying to completely re-write the authentication mechanism would be jumping through painful, complicated hoops.
Some links:
http://www.devx.com/asp/Article/29256/0/page/3
http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx
http://msdn.microsoft.com/en-us/library/f1kyba5e%28v=VS.90%29.aspx
The properties you must implement may look daunting, but unless you need a specific functionality (such as ResetPassword), you can simply throw a NotImplementedException. Code only what you'll use.
Why not just do it the way microsoft recommends?
http://msdn.microsoft.com/en-us/library/9wff0kyh.aspx
I've done custom authentication this way and it works fine.
Here is another link which should prove useful:
Link
What you have done with IHttpModule seems like a good path to tackle this kind of issue. One of the purposes of the http module as stated by microsoft is to enable for any kind of special authentication. When http module intializes it uses the same instance for new requests. Since you dont have any global variables I am not so sure how to address your thread safe question. It seems like you are onlu reading some data out, so please elaborate!
I was wondering if anyone would be able to help me with the following?
I need some more complicated rules for authorisation in a webapp than just role, which I have working fine. Something along the lines of "Allow all Admins. Allow Buyers, provided they have the correct department ID and are allowed to see this customer's credentials".
I am using a custom identity and custom principal to store information such as whether a user is allowed to see all clients or which individual clients they may see. This information is retrieved from a database and added upon creation of the identity/principal.
I have created a custom permission that extends IPermission, ISecurityEncodable. Within this, I have modified the Demand() function to the following:
public void Demand()
{
this._identity = (UserIdentity)Thread.CurrentPrincipal.Identity;
if (Thread.CurrentPrincipal.IsInRole("Admin")) { }
else if ((Thread.CurrentPrincipal.IsInRole("Buyer")) &&
(this._identity.CanViewAllClients) &&
(this._identity.IsInDept(this._departmentID)) ) { }
else if ((Thread.CurrentPrincipal.IsInRole("Buyer")) &&
(this._identity.CanViewClient(this._requestedClient)) &&
(this._identity.IsInDept(this._departmentID)) ) { }
else { throw new SecurityException("Custom Permission Denied"); }
}
I then call this when I wish to authorise by using
CustomPermission custperm = new CustomPermission(requestedClient, reqClientDept);
custperm.Demand();
This works fine, but seems a messy, hacky way to do things. Especially since it would be nice to use my security roles as an attribute e.g.
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
public class...
Perhaps there is a way to call [CustomPrincipalPermission(SecurityAction.Demand, Authorised = true)] with a custom IsAuthorised check? Is this possible? What would need to be implemented?
I apologise if there is a simple solution that I've missed online, but rest assured I have been checking for days now.
It seems like what you want is a declarative rather than a programmatic demand. In order to do so, you'll need to create a CustomPermissionAttribute based on your CustomPermission.
There's an example of a declarative demand of a custom permission here and details of creating a custom permission attribute here.
In my current asp.net mvc project a user should be able to log in. When logged in a set of preferences can optionally be set. Some of those preferences are general (e.g. prefered site language etc), but some are specific to this project only (pre-defined query filtering etc).
Since these preferences are present in a lot of different places of my site I would define this as cross-concern. Preferably I would have an attribute handle this rather than every action on it's own.
How can I design such an attribute that is generic enough to re-use in future projects yet knows enough about the current project to use all the project specific settings too?
--EDIT--
Getting and setting the preferences is not the problem. I connected a UserSettings class to the asp.net profile provider.
My problem is how to pull this cross concern out of my controllers into an attribute.
independently if you are storing that preferences in a text file, xml or database, wouldn't be easier to create a class (for example Utility.UserPreferences) that will load those preferences from the user and store them in a Session variable, then using an enum call them to retrieve / update
namespace Utility
{
public class UserPreferences
{
public UserPreferences(int userID)
{
// Load Preferences from text file, xml file or DB
string loadedPreferences = "us|20|yes|no";
HttpContext.Current.Session["userPreferences"] = loadedPreferences;
}
public void Savepreferences(string[] pref, int userID)
{
// Save preferences for that user
}
public static string GetPreferences(PreferencesType type)
{
string[] pref = HttpContext.Current.Session["userPreferences"].ToString().Split('|');
switch (type)
{
case PreferencesType.Language: return pref[0];
case PreferencesType.ShowHowManyResults: return pref[1];
case PreferencesType.ShowNavigation: return pref[2];
case PreferencesType.GetEmailAlerts: return pref[3];
}
}
public enum PreferencesType
{
Language, ShowHowManyResults, ShowNavigation, GetEmailAlerts
}
}
}
then ...
// Login sucessfully...
Utility.UserPreferences pref = new Utility.UserPreferences(CurrentUser.ID);
// to retrieve a preference
string language = Utility.UserPreferences.GetPreferences(
Utility.UserPreferences.PreferencesType.Language,
CurrentUser.ID);
it's just an idea... the way I would do it... it's simple and it's project(s) wide, cause you just need to change the class to hold more preferences...
For user preferences you should use the ASP.NET Profile Provider framework. Some resources:
ASP.NET Profile Providers
Implementing a Profile Provider
StackOverflow: Implementing Profile Provider in ASP.NET MVC
StackOverflow: Implementing a Custom Profile Provider in ASP.NET MVC
You could build attribute-based user preference handling on top of the Profile provider framework, but I imagine that sort of thing would be specific to your app.