storing user data in static class - c#

I am creating a login system and I want a way to sort of cache information without retrieving the same information from the database.
for example I would have a static class called tokenData. token data would be a private class to store login token, username, expireDate, etc. So every time I visit another page it would check the static class for the data. The token is then stored in session / cookie to produce the lookup. If the data is not in the token static class (e.g. application pool restart) then it would check the database for the record when the user logs in and creates another based on the data in the token table.
Can someone offer me any advice is this is acceptable practice or offer me anything to improve and issues that can arise?
an exmaple is
public class userToken
{
private string name;
private string tokenId;
private static List<userToken> userData = new List<userToken>();
public void add(userToken);
public userToken Find(string tokenId);
}

Never ever ever use static for user or session specific data. static is shared across ALL sessions! You might end up with user sessions sharing confidential data.
Use HttpContext.Session or HttpContext.Cache.

Your solution can introduce errors when run on more than a single server with a single user. The cache you are building is not thread safe. It will also introduce errors when your app is run across 2+ servers in a cluster (load balanced).
I would look into using a proper caching toolset (memcached, etc.)

Related

Static class storing current user in web application

I'm new to web application in asp.net mvc 5. I'm curious about how static classes behaves in web application. I'd like to know how my program will behave.
Let's say I have CurrentUser static class which stores logged user id.
public static class CurrentUser{
public static int UserId {get; set;}
}
Which is set whenerever user is logging in.
My app is in external server.
So what will happen if:
User A log in -> userId is set to 1, then User B log in (they access to from differentlcoations) so user Id is set to 2. When User A would like to perform action which need to check his Id, will it be 1 or 2?
I checked one scenario where 2 differentpersons log in from one pc at the same time (different tabs) and I know that User Id will be 2 for both of them (when User B logged in as second to the app). How to resolve this?
I've already read: Static classes in web applications.
I know that my solution may be error prone because every one has access to that class but I don't know if static classes in web app aren't store per user (thread?)?
If you store current user in session storage it will be better than static class. Because there is one copy of static class and fields and for every user login the last login is kept.

Use static dictionary in asp.net web application

I use SignalR in my asp.net web application. Session state doesn't exist in SignalR hub, so I decided to save username <-> connectionId pair in static dictionary.
public static class UsernameConnectionsMaps
{
private static Dictionary<string, string> data = new Dictionary<string, string>();
public static void Add(string username, string connectionId)
{
data[username] = connectionId;
}
public static string Get(string username)
{
return data[username];
}
public static void Remove(string username)
{
return data.Remove(username);
}
}
On a particular controller request, I want to send data to the client with signalR. Knowing the username of current user, I can easily get connectionId for the client and send data.
I use Get method in my controller.
I use Add and Remove methods in hub's OnConnected() and OnDisconnected() methods respectively.
I am interested in, if this solution will have any problem regarding thread safety (or other) in my web application? What will be better approach?
If you use a static dictionary you will lose the ability to scale out your API.
Each instance will keep a different static Dictionary, so notifications will not always reach their destination, consider azure signalr or some kind of persistent storage like redis.
The best you can do and it is what is said in the Microsoft documentation, map users to groups, even if it will produce groups with single users or a groups with many users that are actually the same user. Even if you will use Redis backplane or Azure SignalR, you will have the groups in all context and can communicate with them, add, remove and etc...
A single user can have multiple connections to a SignalR app. For example, a user could be connected on their desktop as well as their phone. Each device has a separate SignalR connection, but they're all associated with the same user. If a message is sent to the user, all of the connections associated with that user receive the message.

Microsoft Bot Framework Multi-Tenant Credentials C#

Background
I've created a working bot in C# but I'm failing to expand it to be a multi-tenant bot. I have created multiple bots in the Microsoft portal using this technique to identify themselves from the messaging endpoint:
https://example.com/api/messages/bot1
https://example.com/api/messages/bot2
https://example.com/api/messages/bot3
I can grab the LastSegment from the URL while in the MessagesController and store it in PrivateConversationData so I know which bot is talking in the current conversation. I intended use this stored 'bot id' in order to retrieve the Microsoft AppId & Password from the web.config (the bot's credentials are stored as a series of custom entries and not the standard appSettings as that only works for a single bot).
Credentials Problem
The authentication works well (nearly) as described here except when using async code with .ConfigureAwait(false) I can't get the HttpContext.Current as it becomes null when running on a different thread. This means I can't get the authenticated user's credentials either by looking them up in the web.config or by calling GetCredentialsFromClaims() since I've lost the authenticated user. If I use .ConfigureAwait(true) I just get deadlocks all over the place.
I have the credentials in the web.config but they are stored per bot and I need the 'bot id' from the URL above in order to get the credentials.
Question
The crux of the problem is: I need the URL to get the 'bot id' and I need the 'bot id' to get the credentials from the web.config but I can never reliably get access to the URL once I've passed a .ConfigureAwait(false) in the code. On the flip side, I can't get the 'bot id' from the PrivateConversationData since I need the bot's credentials in order to load it. A bit chicken and egg :-(
If anyone has any ideas of what I may be doing wrong or has an alternative approach to know which 'bot id' is currently executing I'd very much appreciate it.
Thanks
Please find below given the sample code.
public class StartUp {
public void Configuration(IAppBuilder app) {
var builder = new ContainerBuilder();
//Note: Initialize / register the Metadata Service that can bring the tenant details from the corresponding store
builder.RegisterType<TenantMetadataService>().As<ITenantMetadataService>();
//Note: This helps you in accessing the TenantMetadata from any constructor going forward after the below registry
builder.Register(ti => TenantMetadata.GetTenantMetadataFromRequest()).InstancePerRequest();
//TODO: Register the various services / controllers etc which may require the tenant details here
}
}
public class TenantMetadata {
public Guid TenantId { get;set; }
public Uri TenantUrl { get;set; }
public string TenantName { get;set; }
public static TenantMetadata GetTenantMetadataFromRequest() {
var context = HttpContext.Current;
//TODO: If you have any header like TenantId coming from the request, you can read and use it
var tenantIdFromRequestHeader = "";
//TODO: There will be a lazy cache that keeps building the data as new tenant's login or use the application
if(TenantCache.Contains(...))return TenantCache[Key];
//TODO: Do a look-up from the above step and then construct the metadata
var tenantMetadata = metadataSvc.GetTenantMetadata(...);
//TODO: If the data match does not happen from the Step2, build the cache and then return the value.
TenantCache.Add(key,tenantMetadata);
return tenantMetadata;
}
}
Note
The above code snippet uses the various service placeholders, cache and the other methods which will require to be used based on the designed application services. If you wish not to cache the tenant metadata, if it may contain some sensitive data, you can remove the caching implementation parts.
This implementation can be spread across all your web facing portals like your Web UI, Web Api and WebJobs etc so that it is same across all apps and it is easy to test and consume.
HTH.

How do I make this object exist within sessions?

Here's the deal: I have a site where multiple people will be sharing the same account and should each be able to be on a page that uploads files and keeps a list of the files they've uploaded that session. The controller for the file uploading page looks like
public class FileUploadController : Controller
{
// ...
private List<ThreePartKey> uploadedFiles = new List<ThreePartKey> ();
public ActionResult Index ( )
{
// ...
}
[HttpPost]
public ActionResult Index (HttpPostedFileBase file)
{
// ...
if (!errorOccured)
{
uploadedFiles.Add(new ThreePartKey { orgname = selectedOrgName, catname = selectedCatName, filename = fileNameNoExtension, fullfilepath = newFileUrlPathAndName });
}
// ...
}
and the problem is that uploadedFiles keeps getting re-initialized whenever [HttpPost] public ActionResult Index (HttpPostedFileBase file) is called, meaning the user's list of uploaded files only shows the last uploaded one. So I instead tried
private static List<ThreePartKey> uploadedFiles = new List<ThreePartKey> ();
and that screwed up everything because all the signed-in users are sharing the same list.
Is there any easy way to do what I'm trying to do?
Controllers are instantiated and destroyed on every request. If you want to persist information in a webserver it is strongly advised to use a permanent backing store such as a database.
You can use static state in ASP.NET applications (WebForms, MVC, OWIN, etc) however this is only recommended for caching for performance. It cannot be relied upon because static state is only local to the current AppDomain in the current Application Pool (w3wp.exe instance) - if your website is run in multiple pools or appdomains, or if your application is restarted (or killed due to inactivity) then the stored state is lost.
On option is to provide a 'session' code/id with each request. When user first connects to your site, they are given a session-code (I use 'code' to indicate it has nothing to do with what we would normally call 'session').
Every link has that session-code as part of the url and every post includes the session-code. Then your upload cache can be:
private static ILookup<int, ThreePartKey> uploadedFiles;
(or dictionary if you prefer)
private static IDictionary<int, IList<ThreePartKey>> uploadedFiles;
Depends on the size of the rest of your site if this is workable or not - in most cases probably not as described... but could be managed, eg use the IP address as the 'code' or if you're using AnglurJS or single page application.
As pointed out, any static/singleton cache will still be lost if the app pool is reset, eg via the inactivity timeout setting in IIS.
Another option is to persist the files in subfolders based on the user's IP address.
You've only stipulated that they all use the same login, not how the files are stored etc, so maybe this would work for you.

Providing custom database functionality to custom asp.net membership provider

I am creating custom membership provider for my asp.net application. I have also created a separate class "DBConnect" that provides database functionality such as Executing SQL statement, Executing SPs, Executing SPs or Query and returning SqlDataReader and so on...
I have created instance of DBConnect class within Session_Start of Global.asax and stored to a session. Later using a static class I am providing the database functionality throughout the application using the same single session. In short I am providing a single point for all database operations from any asp.net page.
I know that i can write my own code to connect/disconnect database and execute SPs within from the methods i need to override. Please look at the code below -
public class SGI_MembershipProvider : MembershipProvider
{
......
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
if (!ValidateUser(username, oldPassword))
return false;
ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
OnValidatingPassword(args);
if (args.Cancel)
{
if (args.FailureInformation != null)
{
throw args.FailureInformation;
}
else
{
throw new Exception("Change password canceled due to new password validation failure.");
}
}
.....
//Database connectivity and code execution to change password.
}
....
}
MY PROBLEM -
Now what i need is to execute the database part within all these overriden methods from the same database point as described on the top. That is i have to pass the instance of DBConnect existing in the session to this class, so that i can access the methods.
Could anyone provide solution on this. There might be some better techniques i am not aware of that. The approach i am using might be wrong. Your suggessions are always welcome.
Thanks for sharing your valuable time.
Understanding the lifecycle of the membership provider would help clear this up.
An instance of the membership provider is spun up when the application is started and remains active for the lifetime of the application AppDomain, which in practice equates to the application lifecycle. e.g. If for some reason the AppDomain cycles, the application is disposed and a new instance is spun up. A new instance of the registered membership provider is spun up on first use.
You need to either instantiate an instance of you data access class within your membership provider implementation or access static methods from within your provider. I prefer to use an instance.
Separating the membership provider from it's data access by creating singletons or stashing it in application is a hack in my opinion and will lead to nothing but pain, sorrow, lost sleep and credibility amongst your peers.
Cheers and good luck.
Dont keep a seperate instance of the DBConnect class in session, you will end up creating a class for each user! This will seriously affect scalability.
You can do one of the following :
Place the class in Application state
Use the singleton pattern
Make the class and all the methods in the class static.
My recommendation is to go for number 3. You dont usually need to create an instance of a class that does database crud operations eg
public static class DBConnect
{
public static ChangePassword(string userId, string password)
{
//Implementation here
}
}
Then you can simply call this code in your provider without creating an instance:
DBConnect.ChangePassword(x,y);

Categories