Yii RBAC, Role change in runtime - yii

I am building up a dynamic RBAC system for Yii and I don't know how to handle this problem:
The moderators can change the roles of the Users, furthermore the User can change it too by getting a different qualification (let's say achievement, so s/he can do more stuff and it can happen both ways).
What happens, when the role is changed Backwards (a role with less right) or Forwards (a role with more right) when s/he is logged in? Cannot access the functions he just got the right to use? Or can still access the functions until a logout/relog action?
Thanks your help in advance.

The effect of changing the authorization assignment will be inmediate.
Only the successive calls to IWebUser::checkAccess() issued in the same request may return cached values, since the default implementation of IWebUser, i.e. CWebUser, uses a static attribute to cache the calculated permissions.
To clarify the procedure, you will be calling IAuthManager::revoke() on the old permissions and IAuthManager::assign() on the new ones.
Edit
Sometimes you store session information through the IWebUser::setState() method; if the state of the currently logged user shall change along with the permissions, e.g. you store the current user's role name, you must take this into account and either call IWebUser::clearState() or IWebUser::logout() followed by IWebUser::login() –the latter also clears the cached permissions in the CWebUser implementation.
CWebUser::_access is declared private, so you will have to declare a new attribute if you want to override the default implementation.

Related

Best way to do conditional access to a controller action method based on provided parameters?

I have an application where each resource has multiple "sites". A user can have permission to access this resource for a number of these sites or none at all.
I have middleware set up that reads a user from a jwt and adds that user to the context. I then have an authorize attribute that simply checks for the existence of a user.
This is where I could use some help. I am not sure of the best way to conditionally restrict access to the resource (the condition being which site the end-user is specifically requesting the resource from).
I could break the resource into separate action methods and use an attribute that requires a specific role, but that feels a bit too much like duplication, I'd rather not deal with 7 endpoints that basically do the same thing, and adding new sites in the future will require adding a ton of new action methods.
Another solution I had in mind is to create an attribute/filter that compares the end-users permissions to the specific site that they are requesting. This sounds better but I'm not sure how to access specific arguments on the action method from the attribute. Also not sure of the best way to store all of a users "Roles" on an entity class...
Any insight is appreciated.

What's the expected behavior of a dependent app when an LDAP user's DN changes?

Is there any expected/standardized behavior for an app that authenticates via LDAP, when one of its registered users has its DN changed? (Or more generally, when their effective principal changes, which seems to commonly be their DN. I'll say "principal" instead from here, though I usually think "DN".)
Here's an example from Gitlab running into this issue: https://gitlab.com/gitlab-org/gitlab-foss/issues/993
In their case (based on the commit linked in that issue) they seem to address this by using multiple attributes to identify users instead of a single one, so that a single change doesn't prevent them from mapping back to the same LDAP user.
It seems to me the available options would be:
Don't try to prevent the issue but provide some method to change the principal stored (either in-app or via DB modifications)
Try to prevent the issue by picking (automatically or manually by user) a better, static user attribute to use as principal
Try to mitigate the issue by cross-referencing multiple attributes, as in the above links
Which of these would be the expected/"best" approach here, if any?

Using User.IsInRole returns random result when UserRole changes

I'm using asp.net core 2.1 with default identity settings and every time a role is changed the user should re-login to see the role changes.
If I add the following settings, roles should be updated on each request, the Authorize attribute works well but User.IsInRole() method returns random results on each request.
services.Configure<SecurityStampValidatorOptions>(options =>
{
options.ValidationInterval = TimeSpan.Zero;
});
What's the problem with User.IsInRole()? How to fix it?
Edit:
I was incorrect about the Authorize attribute behavior, that works random too.
Edit:
a test project to reproduce the issue (warning: it's using InMemory db) -> https://github.com/ans-ashkan/AspNetCoreUserIsInRoleTest
http://localhost:5000/users/getall -> 200: ["test"]
http://localhost:5000/users/signin?username=test&password=123 -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":false,"identityName":"test"}
http://localhost:5000/users/adduserrole?username=test&role=admin -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":**random true or false**,"identityName":"test"}
http://localhost:5000/users/signout -> 200
http://localhost:5000/users/signin?username=test&password=123 -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":true,"identityName":"test"}
Edit:
Issue link on AspNet/MVC repo
I’ve investigated this problem in detail and posted my findings in the issue. It is indeed a bug, but it actually does not have that much of an effect on your problem:
Changing a user’s roles will not invalidate the user’s security stamp, so the issued claims identity that the user receives through the cookie is not actually invalid. So if you want to invalidate the identity when the roles change, you will have to look for a different solution.
However, in my opinion, you are completely misusing this: If you want to refresh the identity and its claims on every single request, then there is really no point in actually having a claims identity at all. Running the authentication stack is not free, and having to run the whole pipeline of validating and reissuing will be quite expensive. And when you don’t actually want to store the identity for a longer time anyway (because you invalidate it right in the next request), then that’s really wasted work.
So if you really need a permission system that are absolutely sharp and updated right in the moment when they change, then consider using something different. You could set up a separate database and just store the “roles” there, and then, when you access something protected, you just fetch the user’s roles on demand there to verify access. That will also help you from fetching the roles all the time on every request since now you would only fetch it when you need it.
Of course, you don’t need a separate database for this. You could also use the built-in roles of Identity. You just need to remember then though that the role claims are not always the source of truth, so you should always load the user’s roles from the database (through the user manager).
You can actually design this pretty well with ASP.NET Core’s authorization stack. For example, you could create a requirement for a role and then implement an authorization handler that checks the role by going through the database. That way, you can make this as transparent as using role claims for users. E.g. you could just use the same Authorize attribute which you would be using otherwise.

Claims-based auth and circles of users

Let's think of a simple REST-based web service to store and retrieve JSON objects. Without any authentication, every user can access any data.
Now let's add usage of JWTs to authenticate users. Of course, since we are not doing authorization, still every user can access anything, but at least now we know who accesses what.
Next step: When storing an object, save the user's sub claim with the object, and verify that the user's sub claim matches the one of the requested object, and if so, deliver it. Now we have authorization on a per-user basis. So far, so easy.
Now let's add the possibility to let users share objects with other users. For the sake of simplicity, say, we want to have predefined groups (i.e., roles) and we want to let the user choose which group (role) has access to the objects they create. This is still easy, as you have a predefined list of groups, so you can let the user choose one or more, and attach them to the object. Additionally, the identity provider needs to be configured in a way that it put a groups claim into every user's token, so we can match them. As we can already see from the length of this paragraph, things become more complex.
Question 1: Am I right so far, that handling "static" groups this way it the way to go?
Now, let's give the users the opportunity to create groups on their own. This is still not complicated, but how do we make the identity provider use the dynamically created groups? For sure we do not want to make an administrator update the identity provider's configuration every day ;-).
Question 2: How do we handle dynamically created groups?
Now, finally, let's forget about groups, and let's say, that we want to allow the users to be able to simply share their objects with other users. This should be configurable for every object individually. How do we do this? Do we save a list of users on the object? If so, what exactly do we save? The sub claim? If so, how does the owner user know the appropriate values? Or ...?
And: Supposed the users want to put their friends dynamically into dynamically generated circles, how would we do that?
Question 3: How do tokens and dynamically created groups with dynamically assigned users work with each other?
In my opinion the tokens should only include identity-information(-claims) you'll need to identify the user on your ressource server or that wont change when refreshing, since you wouldn't want your user (or your app in place) to have to refresh his access tokens each and every time his permissions change (since access tokens grant access for a specific amount of time you wouldn't want your user to have access to certain ressources that he's lost the access rights to since his last token refresh by not refreshing the token and simply using the old one either). The more security related information you append to your token, the more vulnerability you may add to your system (depending on your token lifetime).
Stating so, I would (and always do) identify the users roles or groups by his user-id (which is included in the jwt-token) on the ressource server. To achieve this, I always attach the users identity-information to the current request on my ressource server and attach "dynamic"-claims like the users role or group to the identity.
By adding only the identity information I need to identify the user and the user's rights on my ressource server, I tend to use my identity providers across multiple applications without handling application scope on the identity provider, so I can use the exact same access token for multiple independent ressource servers.

MVC 4 : Passing around user group data

I am in the process of rewriting my PHP website in ASP.NET and writing the membership system.
I understand I can extend MembershipUser to add member specific properties but how can I pass around boolean group information such as Use Search, Edit Posts etc which are not user specific? Is there a framework item I am missing or should I just create a super object to pass this and other settings around?
Essentially what I want it an efficient way to access the users group properties in my controllers.
Apart from extending the MembershipProvider, you can also extend RoleProvider. RoleProvider is in charge of checking to which group a user belongs to, registering new roles, adding user to role(s), etc. To work with roles you will use Roles class which contains a lot of static methods.
In addition to this, each time you hit a Controller, you can query HttpContext.User property which implements IPrincipal. This property has method IsInRole that is used to communicate with RoleProvider to obtain information if a user is in specific group or not.
Also, in order to allow access to controllers or actions you can use Authorization attribute and list specific roles that have access to the controller.
The roles can be stored in a cookie (to cache them) or you can implement Application_AuthenticateRequest in global.asax and initialize GenericPrincipal manually. This object is passed over to HttpContext.User. The constructor of this object accepts an array of roles that are queried with IsInRole method.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// Check if user is authenticated
if (HttpContext.User != null)
{
// Extract roles from a cookie if you used FormsAuthentication
// or read them from a cookie or from some other cached location
// Split roles into array of strings
var roles = listOfRoles.ToArray(); // If it is stored in a List<string>.
var identity = HttpContext.User.Identity;
var principal = new GenericPrincipal(identity, roles);
HttpContext.User = principal;
}
}
The above code is not tested. I wrote it from top of my mind. It should give you a pretty good picture how to cache roles and to use them in the most efficient way.
UPDATE: In case that you need more advanced options where each role can have one or more functionality like your "Use search", "Can do something", "Can do that", I would implement the following security logic:
Users
Roles (users belong to roles)
AccessRight (Role can have one or more access right).
UsersRoles table would be for adding users to specific roles.
RolesAccessRights table is where you define specific rights to each role.
User never talks to Functionality. (BTW, this naming convention is just an example, you will follow your naming conventions).
At my last work this is how we implemented the Audit system (it was Web Forms based). However, in MVC you could override AuthorizationAttribute to check user's role and to check if Role has defined access rights. Considering that you have specific security requirement, you would have to use this attribute on every action where you see the need and necessity.
If you plan to implement this logic, forget about Membership, MembershipUser and Roles. Honestly, I don't use these classes any more. I have my own custom security that I implement and which I used in the last 4 projects without any need for update or modification.
UPDATE 2: The security solution that we used was based on custom MembershipProvider and RoleProvider. Thinking about it now, it was a mistake to rely on that because access to AccessLevel table had to be mapped via Entity Framework. Therefore we had to ways to query our security tables.
My suggestion to you would be to ignore Membership- and Role-related classes completely. The first reason is that you would avoid bothering yourself with unnecessary methods and properties when you override the providers. There would be too many methods with throw new NotSupportedException() in the method body.
Suggested implementation
You will need the following tables:
Users - (You need at least three columns UserId, UserName, Password). If you want to hash the password, you might have to store salt as well. Other columns like FirstName, LastName, etc. I would suggest you to store in a different table and link it with UserId. As for UserId type it's up to you whether you would use int or Guid.
Roles - (You need at least two columns RoleId, RoleName). Again, as with UserId, it is up to you which data type you want to use.
UsersRoles - Store UserId and RoleId. You might want to store properties such as whether the role IsActive which is a bit value.
AccessRights - This is where you would store a key of your access right. In your case that is like UseSearch, EditPosts, DeletePosts, etc. Here you should use at least three columns AccessRightId, AccessRightKey and AccessRightDescription. This description field will turn to be pretty valuable if you have a lot of access right keys.
RolesAccessRights - This is where you define to which role you have added specific access rights. Also have IsActive bit value in order to disable the specific access right to a role.
In MVC you would override AuthorizationAttribute. In this attribute you would specify a list of access rights that have access to controller and/or actions. How you plan to do this is entirely up to you, but I would create an enum with a list of values that are the same as AccessRightsKeys. That way you can use strongly typed access rights instead of string based list. For more information about implementing custom authorization attribute have a look at the references list.
Inside of this attribute, you would read User ID and retrieve the roles. Compare the AccessRightsKeys that you specified against the roles (RolesAccessRights table) to see if the role has access right and whether the rule is active.
As for the solution based implementation I would implement Security service layer which communicates with Security-based repository and unit of work solutions. Because you are using MySQL I don't know which ORM you can use or would you have to rely on ADO.NET with OLEDB providers for MySQL.
My usual approach is a top-down approach. I implement from the high up (like Presentation layer) and go down towards data access layer. That way at the end I have only those methods which I really use and there is no redundancy.
Well, I hope this gives you some picture on how to this. As for time it takes, you can do this in about 8-10 hours.
Reference:
Implementing a Role Provider
Roles Class
How to: Create a Custom AuthorizationAttribute