Web Api User Tracking - c#

I am in need of help with Web Api.
I am setting up a multi tenant system when each tenant has there own database of data using code first EF and web api (so that I can create multiple app platforms)
I have extended the standard ASP.NET Identity to include a client id and client model which will store all tenants and their users.
I have then created another context which tracks all the data each tenant stores.
Each tenant holds a database name which I need to access based on the authenticated user.
Not getting the user id from each api controller seems easy:
RequestContext.Principal..... etc then I can get the client and subsequently the client database name to pass to the database context however I am trying to implement a standard data repository pattern and really hate repeating myself in code yet the only way I see it working at the moment is to:
Application calls restful api after authorisation
Web Api captures call
Each endpoint gets the user id and passes it to the data store via the interface and subsequently into the data layer retrieving the database name for the context.
What I have a problem with here is each endpoint getting the user id. Is there a way to "store/track" the user id per session? Can this be achieved through scope dependency or something similar?
I hope that makes sense but if not please ask and I will try to clarify further, any help will be greatly appreciated.
Thanks
Carl

ASP WebApi does not have a session context. You may use a cookie or a request token identifier (pass this token back from login and use this token as a parameter for further API calls).
This is something I've developed some time ago. I'm simply creating a new class deriving from ApiController and I'm using this class as a base for all other API class. It is using the ASP.NET cache object which can be accessed via HttpContext. I'm using the current user-id as a reference. If you need something else, you may use another way of caching your data:
public abstract class BaseController: ApiController
{
private readonly object _lock = new object();
/// <summary>
/// The customer this controller is referencing to.
/// </summary>
protected Guid CustomerId
{
get
{
if (!_customerId.HasValue)
{
InitApi();
lock (_lock)
{
if (User.Identity.IsAuthenticated)
{
Guid? customerId = HttpContext.Current.Cache["APIID" + User.Identity.Name] as Guid?;
if (customerId.HasValue)
{
CustomerId = customerId.Value;
}
else
{
UserProfile user = UserManager.FindByName(User.Identity.Name);
if (user != null)
{
CustomerId = user.CustomerId;
HttpContext.Current.Cache["APIID" + User.Identity.Name] = user.CustomerId;
}
}
}
else
{
_customerId = Guid.Empty;
}
}
}
return _customerId.GetValueOrDefault();
}
private set { _customerId = value; }
}
// ... more code
}
Do not blame me on the "lock" stuff. This code was some kind of "get it up and running and forget about it"...
A full example can be found here.

Maybe I am far from truth but Web API is state less so you dont really have a session to track

Related

Alternative to Session Variable

I am trying to find an alternative to using a session variable. In my solution I have a project that is referenced by an ASP.NET web application and a console application. Both these applications can make changes to data and when a change is made the ID of the user making the change is logged against that row.
So if it was just the ASP.NET app making changes, it could do something like myObj.LastUpdatedByID = Session["userid"]. Given that the command line app needs to make changes and doesn't have a session, what alternative could I use that has the equivalent of session scope in ASP.NET but is also available in the console app scope?
I've had a look at MemoryCache, but that seems to be application level in ASP.NET.
I don't want to go down the line of passing the user ID through to each call.
Would doing something like checking for a HttpContext and if there is, pull from the session and if there isn't, pull from MemoryCahce? Or is there a better way of doing it?
EDIT:
The user ID is specifically set in the console app depending on what action is being carried. The console app is used for automated processes and there are numerous actions it undertakes. So for example, the sending email process would be carried out by user ID 1 and the delete old files process would be carried out by user ID 2. In some instances, the user ID would be set to the user ID that last made the change to that row of data.
EDIT:
Some example code (stripped for brevity). You can see I am using the MemoryCache here, which as I understand would be application wide and therefore not usable in ASP.NET app:
public class Base(
{
private int auditID = -1;
public int AuditID
{
get
{
if (this.auditID <= 0)
{
ObjectCache memCache = MemoryCache.Default;
this.auditID = ((int)memCache["CurrentUserID"]);
}
return this.auditID;
}
}
}
public class MyObject : Base
{
public int LastUpdatedByID { get; set; } = 0;
public bool Save()
{
bool b = false;
this.LastUpdatedByID = this.AuditID;
//Call to DB here...
return b;
}
}
If the data needs to be persistent across application then you can't use Session or HttpContext.Cache since those are dependent on current HttpContext which you don't have in console app.
Another way, would be to store the data in some persistent data store like Database or distributed cache like Redis Cache / Azure Mem Cache

Maintain the model lifetime in MVC application

I am new to MVC and I have very simple problem.
When user login to my application I need to create a specific object (model) for the user for eg UserObject.
This object is unique to current logged in user and should only be disposed when user click on logout.
I don’t know how to maintain the lifetime of the object. As if I create object in Action method of controller class then as soon as the request is finished I lose the reference of the object.
How this should have been done?
The lifetime of your models are only going to be as long as the request. So each time the user goes to another page or refreshes, the MVC framework is going to instantiate a new controller (and model within). Otherwise your server would have a ton of static objects floating around in memory which would use up a lot of resources and wouldn't scale.
In order to manage state, you are going to need to use other methods such as sessions/cookies and a database.
So let's say the user logs in via /User/Login. This routes the request to an action named UserController.Login().
Inside this action, it instantiates a UserModel.
public ActionResult Login(string username, string password) {
var userModel = new UserModel();
if (userModel.Authenticate(username, password)) {
// Setup your session to maintain state
Session["username"] = username;
} else {
return View("Login");
}
return View("LoginComplete");
}
You might want the user model to actually create the session, but I've shown it here for clarity.
The user model authenticates the user, and then you create a session just like you would in a traditional non-MVC site.
Then in subsequent requests, you will want to authorize the user, and use any session data you have to retrieve state information.
public ActionResult SuperSecretPlace() {
var userModel = new UserModel();
string username = Session["username"]
var user = userModel.GetUserByUsername(username);
if (user == null) throw new HttpException(401, "User is not authorized.");
return View("SuperSecretPlace", user);
}
In the action above, the UserModel might do something like query a database to retrieve the user's data so you can pass it in to the corresponding view.
If you want to make life easier, you might want to just use .NET's built in forms authentication:
http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
For more info about the lifecycle of MVC:
http://www.dotnet-tricks.com/Tutorial/mvc/TbR0041112-Asp.net-MVC-Request-Life-Cycle.html
http://www.asp.net/mvc/overview/getting-started/lifecycle-of-an-aspnet-mvc-5-application
Actually what you are trying to achieve is passing model from controller to controller which is not possible. When an action is executed the context of the model object is disposed at the view and it can cannot be passed from controller to controller. You have to create a new object repopulate it and use it to achieve the goal in different controller.If you need the data to be persisted you can use sessions but still you need to create an object of the model in every controller.
The following image is for your reference as to see what to use when passing data between model-view-controller. Please feel free to ask if you need more information on this.
As opposed to the other aswers I would not use session as it has quite some disadvantages (scalability, pessimistic concurrency which blocks concurrent calls, app pool recycling...). Why you should not use session is documented in a lot of places like here or here.
Instead, I would store it in a cookie.
However, be sure to not store confidential or sensitive data. Whatever you use (cookies or session), it can be tampered with or stolen. If you are dealing with sensitive information, you need other solutions. Read also more about secure cookie solution here.

Access session in class library.

I am developing an application architecture that uses 2 sub projects:
a) asp.net web application (it covers user interface and business logic) and
b) class library. (it covers data access layer)
After system user successfully logs in , the user information is stored in a session object.
The problem I am facing is when I try to access that session object in class library project(data access layer), it always returns null.
I need to access the session object in class library project because, in my case each user has their own username and password for database access(for security reasons);
So, How how do i read and write from/to session object in class library project
Use the System.Web.HttpContext.Current.Session object.
First of all, as Peri correctly noticed - you need to think again if having separate database logins for each user is a good idea - because you loose connection pooling (different users won't be able to reuse existing connections - and creating a new sql connection is quite expensive).
If you really wish to keep separate database users, I would create interface to abstract session from data access:
public interface ILoginDataService
{
LoginData Current { get; }
}
And implementation would pass login data from session. In such way you won't have session dependency to session in your data access logic - so it will be more testable, also you'll separate concerns.
Here is the code I used within a library to get session information.
public static string Entity()
{
string entity = "";
HttpContext httpContext = HttpContext.Current;
if (httpContext.ApplicationInstance.Session.Count > 0)
entity = httpContext.ApplicationInstance.Session["EntityCode"].ToString();
return entity;
}
I am having an ASP.Net application which uses session. I am able to access it in my app_code files using [WebMethod(EnableSession = true)] for the function. I am not sure whether this is your problem. I also faced session value as null when I removed (EnableSession = true) on the method.
using System.Web;
namespace ClassNameSpace
{
public class Class1 : IRequiresSessionState
{
private string sessionValue => HttpContext.Current.Session["sessionKey"].ToString();
}
}

Ninject session scope concept in MVC3

I am building a MVC3 app using Ninject framework. I have a service that is time-consuming to initialize, and at the end this service will has an object that contains user-specific information, then I need to re-use that service as long as the user session is active, so that I can avoid to initialize that service again and again
So my question is
When I bind the service using Ninject what kind of scope should I pick, there is no session per scope in Ninject, so what is the best way to implement the requirement? or did I went to a wrong direction at all?
I've created a custom provider for one of my services that will create the service based on username details that is grabbed from current Controller.User.Identity.Name. The code below won't work because the userName local variable is missing, how can I pass the user name value into my custom provider via Ninject, so that I can pick it up from IContext??
public class TfsConnectionManagerProvider : Provider<TfsConnectionManager>
{
protected override TfsConnectionManager CreateInstance(IContext context)
{
Uri serverUri = new Uri(ConfigurationHelper.TfsServerUrl);
// Connect to the server without impersonation
using (TfsTeamProjectCollection baseUserConnection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(serverUri))
{
// Get the identity management service
IIdentityManagementService ims = baseUserConnection.GetService<IIdentityManagementService>();
// Get the identity to impersonate
TeamFoundationIdentity identity = ims.ReadIdentity
(
IdentitySearchFactor.AccountName,
userName, //NOTE: How can I get user name value from IContext???
MembershipQuery.None,
ReadIdentityOptions.None
);
// Connect using the impersonated identity
using (TfsTeamProjectCollection impersonatedConnection = new TfsTeamProjectCollection(serverUri, identity.Descriptor))
{
WorkItemStore store = impersonatedConnection.GetService<WorkItemStore>();
return new TfsConnectionManager
{
Store = store
};
}
}
}
}
A session scope is intentionally not offered in Ninject, because having services in a session state is wrong in almost every situation. You should be very carefully about using session state because it brings a lot of disadvantages.
Try to have a stateless application in first place.
If there is a good reason for having data in session scope then put that data (not the services) into the session state and use services that are in singleton, transient or request scope for the processing (separation of data and functionality).
I turn out to use custom Provider for creating the instance and in the custom provider I checked if it exists in session or not.
The binding is done as following
Bind<IRepository>().ToProvider(new TfsRepositoryProvider());
The custom Provider is below
public class TfsRepositoryProvider : Provider<TfsRepository>
{
private const string SesTfsRepository = "SES_TFS_REPOSITORY";
protected override TfsRepository CreateInstance(IContext context)
{
// Retrieve services from kernel
HttpContextBase httpContext = context.Kernel.Get<HttpContextBase>();
if (httpContext == null || httpContext.Session == null)
{
throw new Exception("No bind service found in Kernel for HttpContextBase");
}
return (httpContext.Session[SesTfsRepository] ?? (
httpContext.Session[SesTfsRepository] = new TfsRepository(context.Kernel.Get<IWorkItemStoreWrapper>()))
) as TfsRepository;
}
}
Okay, you can cache / store the user information in your application and only call the external service if you don't have (recent) user information. In your user information retrieval "layer", you just program those two possibilities.
Where you cache, it entirely up to you. You can store this information for example in a local database.
Apparently I understood you wrong, my apologies (below my original answer).
You can use for example an (abstract) factory that holds a static
member of your service (for example) so it will be reused.
Although depending on your service, this might have some unwanted side
effects (I did this once with Data Services and in an ASP.NET MVC3
application my data context was kinda screwed due to some magic that
happened). All I want to say with this is: be careful and test it
well.

Storing User On Login then Pushing Data On Demand

So I'm using SignalR, it's setup and working correctly on my Website.
Let's suppose user A logs in (I am using the Membership API). When A logs in I am calling the connection from .js located in my masterpage. That will assign this use a specific userId.
Let's say now user B logs in goes does some event and that event needs to notify user A from codebehind.
So what I am trying to do here is notify user B of use A's action from CodeBehind. How will user B know user A's ID and how does the whole thing work? I couldn't find help in the documentation as it does not go into that kind of stuff.
How can this be achieved? Thanks.
I realize this has already been answered, but there another option that folks might find helpful. I had trouble finding info on how to do this, so hopefully this helps someone else.
You can override the SignalR ClientID generation and make it use the membership UserID. This means you do not have to maintain a CleintID -> UserID mapping.
To do this, you create a class that implements the IClientIdFactory interface. You can do something like:
public class UserIdClientIdFactory : IClientIdFactory
{
public string CreateClientId(HttpContextBase context)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
MembershipUser user = Membership.GetUser();
return user != null ?
user.ProviderUserKey.ToString() :
Guid.NewGuid().ToString();
}
}
And then in your global.asax:
SignalR.Infrastructure.DependencyResolver.Register(typeof(IClientIdFactory), () => new UserIdClientIdFactory());
EDIT -- as nillls mentioned below, things have changed in the signalR version 0.4. Use ConnectionId rather than ClientId:
public class UserIdClientIdFactory : IConnectionIdFactory
{
public string CreateConnectionId(SignalR.Hosting.IRequest request)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
MembershipUser user = Membership.GetUser();
return user != null ?
user.ProviderUserKey.ToString() :
Guid.NewGuid().ToString();
}
}
And DependencyResolver has moved:
SignalR.Hosting.AspNet.AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new UserIDClientIdFactory());
Hope this helps someone!
Your app needs to store a mapping of SignalR client (connection) IDs to user ids/names. That way, you can look up the current SignalR client ID for user B and then use it to send a message directly to them. Look at the chat sample app at https://github.com/davidfowl/JabbR for an example of this.

Categories

Resources