Our dba has an SPROC for authentication that returns the User Role only if successful validation occurs.
In my custom authentication class, the ValidateUser method hits this SPROC via an Entity Model & Domain Service, and since the SPROC returns the RoleName on successful validation, I already have an instance of it.
When it comes time to run the GetRolesForUser method in my custom RoleProvider, I suppose I could write another SPROC to grab the RoleName again, but that seems a bit redundant, since I've already retrieved the role. I'd love to be able to cache the User Role in my ValidateUser method, access it in my GetRolesForUser method, and ride off into the sunset. Not only would this save time writing an SPROC, but it would also limit the number of db calls the app is making.
Thoughts?
Scott
Thats correct. When you are running your custom RoleProvider, you already have the session state which you can use to store the value of "RoleName".. Alternately, you can extend the MembershipUser call to include a string field for "RoleName" which you can populate in the first call and then assign back to the context.user.
I would not suggest Cache object here becuase the Roles would be tied to a user.
Related
I have few BL functions that checking who is invoking a method by reading the data from httpContext.User property. It works great when the user really SignIn to the system.
I would like to allow users to schedule an action to be run on a later time. Once the user asks to send an email later, i am writing the request to the DB and the automation service i developed will execute it on the right time by making an http call like: https://mysystem.lan/SendEmail/?Requestid=122d8f273465.
since the automation process not happens in the client side, the user is not logged in to the system so the action SendEmail will not run because the httpContext.User property will be null.
Thanks to the parameter Requestid in the url, i can resolve the UserId who created the request and i want to mock the ASP.NET Identity to be the identity of that user and that the GetUserId() extension method will work too (since mocking not affects that kind of methods).
All the answers i saw here explaining how to mock the identity by creating an instance of the controller for unit testing but this situation is different because i need to mock the identity inside inside the controller action when the server is getting the request https://mysystem.lan/SendEmail/?Requestid=122d8f273465.
Can you suggest how to do that?
I have implemented a custom principal approach as outlined here under Step 5: Using a Custom Principal
I then retrieve the user credentials from the database for use with the custom principal, but this results in a database call for every request, so naturally the answer would be to save my user object somewhere, either Session or Cache.
However, it would appear that HttpContext.Current.Session cannot be accessed from within
Application_OnPostAuthenticateRequest, so Cache would seem to be the way to go
The problem is these two answers here and here offer contradictory advice. The first one advises
No, don't use HttpCurrent.Current.Cache to store user specific information as the cache is common for all users and you will get conflicts. Use HttpContext.Current.Session instead as this will be specific to the user.
and the second one advises
Use the Cache instead of session
So which is the preferred method?
If Session is the way to go how do I put my user object into the Session object from the Application_OnPostAuthenticateRequest method.
If Cache is the way forward what problems will I face? For instance, is there a time limit on items held in the Cache? (I know to get around potential conflicts by using unique key from User object)
Not sure if your still looking for a answer but the best place to store the authentication information is in the Ticket.UserData property when writing the ticket.
I am assuming if you are using a custom provider that you are overriding the SetAuthCookie method.
If that is the case then that method will let you pass in the extra information for storing. It's common to store things like friendly username, roles, or other authentication details.
See this link for Setting UserData in Authentication Cookie
I'm using a custom role provider, which to over simplify gets a person object from the database using EF on .net 4 MVC project, and allocates user roles based on some rules around that (and other queries).
The data changes regularly, though changes are mode through code elsewhere in the system, not the roles provider. The roles provider is one way, and simply gets the roles a user is in.
When I change the database values, the role manager does not pick up on the change of roles until I do a recompile (by adding a space in web config for example), or the application otherwise restarts.
I've ensured the roles do not cache in a cookie by setting cacheRolesInCookie=false, which is what most help seems to point to, and presume there is a session cache built into the role manager.
I've modified the EF query which returns the person object to include a time stamp as part of the query. I can see through profiler the query is actually being called, and the time stamp changes each time, but my debug session shows stale data from the previous state for the 'person' item. There are other parts of the site that display data from the Person table, which show the up to date state.
I don't really understand how the debugger should behave on cached data. I don't see why the EF query would fire at all if it's a cache issue, but the person data is definitely showing state as per the first run, not as per the current state of the table row.
I feel I'm missing something obvious. Does the Role Manager cache data in session?
The answer really depends on the architecture of your application. I had this problem recently and was blaming the Role Manager cache as well. Turns out it was the management of the Entity context in my Data Access Layer altogether. I was managing my Entity Context and storing the context per-request as is typically recommended. However the problem was that the context was being set twice due to an unrelated defect, and so the Role Provider's context was always different than the rest of the application, and was only set once (since the Role Provider is instantiated on Application Start, not per-request).
I'd recommend looking how you are storing data contexts and trace through to see how that is being stored in relation to your Role Manager vs the rest of the application. Ensure you are truly only using one context per request.
I have an answer for "Can I clear this cache?"
Yes, you can clear the Role Manager cache.
(note: this method is different from deleting the role cache cookie and allows you to clear the cache during a request).
The Role Manager will cache roles for the current user in HttpContext.Current.User after the first call to the role provider to get the roles.
That cache will be used in subsequent role checks throughout the request, and your custom role provider will not be called.
However, you can force the Role Manager to call your role provider again (and effectively re-grab the roles from the data source) by casting the current user to a RolePrincipal and then calling SetDirty()
For example:
RolePrincipal currentUser = HttpContext.Current.User as RolePrincipal;
currentUser.SetDirty();
See MS Documentation on RolePrincipal.SetDirty Method
I am using the ASP.NET inbuilt login and role management solution (creates table like aspnet_Users etc. and gives access to MembershipUser and the such).
However, at this stage I am a bit stuck with the following:
1) I need to be able to Suspend, Unsuspend and Delete (not necessary remove from table, just disable) users from my app. Is this feature inbuilt?
2) I need to have three different user roles, where one of the roles is always assigned by default. Currently I have built an app with no roles. Is ASP.NET capable of doing this?
ASP.NET Membership has concepts for "Approved" and "Locked out" (after X number of failed log in attempts) for users, you can probably use those features for suspending users. 4guysfromrolla.com had a great article series on Examining ASP.NET's Membership, Roles, and Profile , it's worth a look.
I don't think that's available by default, but should be fairly easy to add in.
Roles are supported in the default implementation. However, you'll have to define and assign the roles yourself.
There is a built-in DeleteUser
method. It calls a stored procedure
named dbo.aspnet_Users_DeleteUser.
You can change that stored procedure
to suspend a user instead of
deleting them.
Similarly, there is a built-in CreateUser method which calls a stored procedure named dbo.aspnet_Membership_CreateUser which you could modify. Or, you could use the Roles.AddUserToRole method to set the default role when the user is created, calling it in your CreateUser method (which would first Membership.CreateUser)
My asp.net mvc site needs some kind of authorization but it is rather different than the usual concept of users and thus membership.
It will be used more for preferences then for authentication actually. Accounts without any password should be possible (and will initially be the only type) and an account can also be used by multiple users at once. Depending on the user group it could be for example that all users of a certain region get a shared account.
This is a decision from the client('s marketing division) and is not up for discussion.
A certain landing page takes (only) a userId in the url that will load up an account which in turn has some preferences linked to it that can be used throughout the rest of the site.
If a user doesn't start at the landing page or the sent accountId doesn't match a record in the system, he/she will be assigned the default account that has default preferences.
I was thinking of not re-inventing the wheel (somebody should find a new expression for this) and use the asp.net Membership system.
But the whole system is based around required passwords, email and single sessions per user, which are all things I can't provide.
Because the situation is a bit unconventional I thought a custom MembershipProvider etc would be in place. But it seems the gist of this is inheriting from the regular Membership classes. The methods of these classes all require things I am not needing.
Any suggestions
You could use the standard Membership provider and using the Built in .Validate() method sending the Username and a Password that is "standard" for all accounts without authentication.
Have 2 different User Controls 1 for "Validated Login with Password" and one for "Share Account without password", each uses Membership-login but the latter needs to have a bit set on the field of the member that says "Public Account = True / 1 "
Good luck, seems like a fun project, would be cool to see the outcome ;)
By the way, you don't need to share the session, or you could, just stored the session in the database and map the session to a user instead of a cookie, might work?
As requested i'll elaborate on different user controls. Briefly i would have 2 Controls, one maybe called GlobalLogin and one called UserLogin, where GlobalLogin displays a Form which only has the Username, when submitted this will trigger a function that uses, as i stated before, a function which calls the Validate method in the Membership provider, with a pre-set password.
As a reflection, see all "Not logged in with password"-users as anonymous and treat them the same way, the only thing that is different is that they can access user-specific areas. This control also needs to check that a certain field in the database is set, such as a "Allows Globally Used Account Without Password"-field, where in this case, the bit / boolean needs to be true for this login to be accepted.
Now to the other part, the Control which handles Password Protected Accounts, this requires both Username & Password and this calls the Validate with these settings. Now, remember that when logged in with password, you can change your password, this SHOULD NOT be possible with a Global Account, because then your global password wouldnt work :)
There is detailed information on the Membership Provider at http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx. Basically you need to create new provider, or derive from the existing, and overload the ValidateUser method to always return true.