I'm creating a practice admin application using MVC4, but I'm not sure the best method to persist the logged in user data for the entire lifetime of the session so that it will be accessible to all views & controllers.
For example, I desire a user to log in, then download the user data from the database, and for the entire session I want to maintain the User model (Name, Database ID etc) so that it's accessible throughout the entire web application until the user is logged out.
Is the best approach to store this data in an encrypted cookie? Or is there a way of using a Static Class?
Currently I've read about using ViewModel Base class like so:
public abstract class ViewModelBase
{
public UserModel User { get; set; }
}
Then all of my ViewModels can inherit the base class, thus providing access to the user data model:
public class AllEmployeesViewModel : ViewModelBase
{
public List<EmployeeModel> Employees { get; set; }
}
However, if in one Controller action I set the user data, it will only be lost when loading another Controller Action.
To me it seems a waste of resources & will increase load times to have to keep downloading the user data from the database in every action.
All advice is much welcome for this new web programmer. If I've missed any important details, please do request it and I will try my best to answer.
You should look into SessionState to store data for the duration of the user's browser session.
http://msdn.microsoft.com/en-us/library/vstudio/ms178581(v=vs.100).aspx
I would abstract the Session for you application in a class in a way it is accessible to your controllers. Maybe an web application core class.
namespace MyApplication.Core
{
public class MySession
{
private const string _SessionName = "__MY_SESSION__";
private MySession() { }
public static MySession Current
{
get
{
MySession session =
(MySession)HttpContext.Current.Session[_SessionName];
if (session == null)
{
session = new MySession();
HttpContext.Current.Session[_SessionName] = session;
}
return session;
}
}
public UserModel CurrentUser { get; set; }
}
}
Somewhere in your login controller logic path
public void SomeLoginFunction(string userName,string password)
{
//DO AUTHENTICATION STUFF
MySession.Current.CurrentUser=aCurrentUserFromDB;
}
In your base class
public class ViewModelBase
{
public UserModel User { get{return {MySession.Current.CurrentUser;} }
}
Related
How to specify restrictions to user in domain driven.
I am using asp.net web api application and that application service uses from web api.
[ApiController]
public class TicketController : ControllerBase
{
....
....
[HttpPost]
[Route("change-date")]
public async Task<IActionResult> ChangeTicketDate(TicketChangeCommand command)
{
var response = await _ticketService.ChangeTicketDate(command, User.Identity.Name);
return Ok(response);
}
....
....
}
To prevent the tickets, I am sending the authenticated username to _ticketService. Because a user may change another user ticket. So I have two rules for ticket date change logic.
1- User should be owner of ticket.
2- Or User sholud be in Administrator role.
(These rules also will be using other services. For example User can only change his own password.)
public class TicketService : ITicketService
{
....
public TicketChange ChangeTicketDate(TicketChangeCommand command, string requestedUsername){
// 1. Check requested user is ticket creator or Administrator
}
....
}
To check the user role and owner, should I create a domain service? Should I create a domain authorization service to check roles?
As I see this in DDD you should have some method in your DomainModel class to do this operation e.g. ChangeTicketDate
Now if you store the information of the ticket creator when you create the ticket "which I believe you do" the validation should be part of DomainModel itself e.g.
ChangeTicketDate(string userName)
then the DomainService "Or ApplicationService" should just pass this value to the Domain Model class and the validation should be in the Domain Class
However, in some cases you may need to implement some validations before the domain model or let's say it's not related to the Entity itself, those validations should be in your Application Service
I guess the following piece of code will explain my idea better
public class TicketService : ITicketService
{
// ....
// I expect that you'll be able to get some information about the current user
public CurrentUserData CurrentUser {get; private set;}
public TicketChange ChangeTicketDate(TicketChangeCommand command){
// 1. Load the Aggregate Root from the Data StoreCheck
var ticket = db.GetById(ticketId);
// 2. Do some Application level checks
// 3. Check if the current user is Admin
if(CurrentUser.IsAdmin)
ticket.ChangeTicketDate();
else
ticket.ChangeTicketDate(CurrentUser.Username);
}
....
}
//Example of Domain Model class
public class Ticket : Entity<Guid>
{
public UserInfo Creator {get; private set;}
public void ChangeTicketDate(string requestedUsername){
if(!string.IsNullOrEmpty(requestedUsername) && requestedUsername != Creator.UserName)
threw new ValidationException("You're not allowed to do ChangeTicketDate");
}
....
}
currently i am working on DMS project in which user can save and retrieve documents.
now i want to set a permission for each user at service level. now i am able to catch each user request in which i got the link of the request,user name and his roll,userid. suppose i have a three roles one is admin which has all permissions and second roll is supervisor.so supervisor can create a user and assign department to assign a department it uses i.e item/assigndepartment. and third roll is clerk i want to restrict to create a user but he can access the department list on department section not in user. to display department list i am using the same request link i.e item/assigndepartment. so how to handle this situation. :(
is it good to store the each roll specific link into table and then check for each request ???
here is my structure.
public class IdentityBasicAuthenticationAttribute : Attribute,
IAuthenticationFilter
{
public string Realm { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
if (context.Request.RequestUri.LocalPath == "/Login/UserLogin")
{
return;
}
// here i am catching the each request from which i am getting the user name,roll and user id.
}
}
and the controller
public class ItemController : ApiController
{
//item/assigndepartment
[HttpPost]
public CategoryList assigndepartment(Departments dept)
{
catList = _itemsModel.assigndepartment(dept);
return catList;
}
}
and model
public class ItemModel
{
publiv void assigndepartment()
{
\\ DB Logic
}
}
If you are building RESTful API, you should have GET and POST methods to item/assigndepartment. Than you can restrict both actions in the controller to their respective roles.
If you are building web app, you can create partial views or view components and render each component depending on the user role.
Given the following masterpage or content page ...............
namespace Intranet
{
public partial class Site : System.Web.UI.MasterPage
{
WebSite.Security.Users.CurrentUser currentUser;
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
currentUser = new WebSite.Security.Users.CurrentUser();
currentUser = WebSite.Security.Users.GetCurrentUser(currentUser);
Label_UserId.Text = currentUser.UserId;
}
}
}
}
that calls the following ............
namespace Intranet
{
public class WebSite
{
public class Security
{
public class Users
{
public class CurrentUser
{
public string UserId { get; set; }
}
public static CurrentUser GetCurrentUser(CurrentUser cu)
{
cu.UserId = "MethodThatGetsUserId";
return cu;
}
}
}
}
}
Will the returned instantiated class 'currentUser' contain unique information even if several different users are on the page at the same time?
Thanks for your time and insight.
Yes, a new class is instantiated for each request even, not just each user.
Static fields in the class will be shared, and you should use session and application data to share data across requests or users.
Nope, with this line:
currentUser = new WebSite.Security.Users.CurrentUser();
You are creating a new instance in your master page class. Instances created in each request are only available in that request (of course, depending on the scope), unless you use static variables. Static variables are the same for all users/threads in your application.
However, what you actually want to do is to get the current user. This should be done using the HttpContext.Current.User or Page.Current which is an IPrincipal and should contain information you filled in the Authenticate_Request method of your application.
To understand more about the ASP.NET forms authentication, please refer to: http://msdn.microsoft.com/en-us/library/9wff0kyh(v=vs.100).aspx
I'm adjusting my web application layers in a way to make the code more testable.
Currently the UI talks to a service locator passing in an interface, this returns the appropriate object based on that type:
ServiceLocator.Get<ISomeService>().ListStuff(arg1, arg2);
Internally, services are instantiated with an instance of IServiceContext and cached.
private static Lazy<IDictionary<Type, object>> _services = new Lazy<IDictionary<Type, object>>(GetServices);
public interface IServiceContext
{
IConfiguration Configuration { get; }
IUser CurrentUser { get; internal set; }
ILogProvider Log { get; }
ICacheProvider Cache { get; }
IProfilerProvider Profiler { get; }
}
public LogService(IServiceContext serviceContext)
: base(serviceContext) { }
I'm happy with the concept and it appears to be rugged enough, my only issue is I want to make the current logged in user available in the ServiceContext but unsure the best way to achieve it.
My thoughts travel along these potential options:
Keep a simple method in the ServiceLocator that handles getting the users session and injects it into the services as requests for them come in.
Move getting the current user out of the IServiceContext and into the ServiceBase base class of each service.
Stop doing this and make each service that needs a user dependent on it.
I appreciate any suggestions, I understand this question my not be in the true spirit of the site.
I have managed 4 days of trial and error to get to this point, just need the last piece of this puzzle.
There's probably many solutions, and I'm not fully sure I understand your question but I'll try to help anyway.
Whenever I need the current user I make a call to static utility class right in the context of the code that uses it. This way I eliminate the possibility of stale information.
You could make a class that implements IUser like
class User : IUser {
private System.Security.Principal.WindowsIdentity identity;
public User() {
this.identity = identity = System.Security.Principal.WindowsIdentity.GetCurrent();
}
public string UserName { get { return this.identity.Name; } }
}
And then maybe:
public class ServiceContext : IServiceContext
{
IUser CurrentUser { get { return new User(); } }
}
I'm developing a DLL project that make transactions with a database server, the only problem is that must be an idependent DLL, and it needs to ask for a login screen if not found any previous information. This login screen needs to be written in C#, just like an Windows Application.
Any ideas would be very apreciatted.
Thanks in advance
This isn't the sort of thing you would typically put into a dll. A login screen would be dependent on the implementing UI. You would be better of creating a data access class that implements an interface that has the login credentials required implemented as properties. This allows any consuming application (web, winforms, wpf, whatever) to create a login screen or pass direct credentials to the data class:
public interface IMyDataInterface
{
string loginUser { get; set; }
string loginPW { get; set; }
}
public class MyDataLayer : IMyDataInterface
{
public MyDataLayer(string login, string pw)
{
loginUser = login;
loginPW = pw;
}
}
Using the interface faithfully guarantees that any exposed datalayer has the same implementation basics for consuming applications.
Edit to reflect more secure method: (idea by #Chris)
using System.Net;
public interface IMyDataInterface
{
NetworkCredential credentials { get; set; }
}
public class MyDataLayer : IMyDataInterface
{
public MyDataLayer(NetworkCredential loginInfo)
{
credentials = loginInfo;
}
}
The constructor can be overloaded to include multiple methods of providing the credentials, but the SecureString class should be used to contain the password in all calls.