I am using MVC4 and what I want to be able to do is to check the status of a user's friend to see whether they are online or not for chat purposes. I have explored the WebSecurity and MEmebership classes but cannot see any functionality within these. Do I have to do something with getting the sessions from the IIS server?
Thanks in advance
There is MembershipUser.IsOnline Property but if this is not useful you can use signlR to check the current status and may be store it somewhere.
Else you can do an ajax call every few minutes to check user status and also store it somewhere.
One possible solution I am suggesting you is to create a global filter that populates LastActionTime field of every user and then use this field to determine whether the user is online or not:
First create a new property of type DateTime? called LastActionTime in your User class in case you are using Code-First approach. In case you are using Database-First approach just add nullable DateTime column in you Users table and update your local model.
public class User
{
// ...
public DateTime? LastActionTime { get; set; }
// ...
}
Then create a new class called LogUserActionTimeFilter and implement it's OnActionExecuting and OnActionExecuted method. If you have any services or DbContexts, initialize them in the constructor of the class.
public class LogUserActionTimeFilter : IActionFilter
{
private readonly IUserAuthService userAuthService;
public LogAdminRequestFilter()
: this(new UserAuthService())
{
}
public LogAdminRequestFilter(IUserAuthService userAuthService)
{
this.userAuthService = userAuthService;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
this.userAuthService.SaveCurrentTimeAsLastActionTime(
filterContext.HttpContext.User.Identity.Name);
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
The SaveCurrentTimeAsLastActionTime method just sets LastActionTime to DateTime.Now of the given user and saves changes. It should be easy to implement.
When done with the filter you can apply it to specific actions, controllers (e.g. in the chat only) or globaly:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new LogUserActionTimeFilter());
}
}
When you need to determine if a user is online or not just check its LastActionTime column/property and if it is less than 5 minutes (for example) before DateTime.Now then the user may be considered online.
Related
We are trying to implement security in with our predefined set of permissions, which will serves the purpose whether to execute action method, show the view OR not, hide specific control(Like button,textbox etc) etc. So, while user getting logged in into the application we have the data of users role and there permissions.
So, my question is whether we should go for ActionFilter OR Authorize Filter? Initially we have tried with ActionFilter, but my action filter is getting called though the particular action is NOT executed/called.
Action Filter
using Microsoft.AspNetCore.Mvc.Filters;
namespace LMS.Web.Core.Classes
{
public class SecurityFilter : ActionFilterAttribute
{
private string permissionName;
private Permissions permissions;
public SecurityFilter(string m_permissionName)
{
permissionName = m_permissionName;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
}
}
and this action filter I have referred on one action method
[Route("Course/Info")]
[SecurityFilter("some permission name")]
public ActionResult CourseDetails()
{
return View();
}
So, while logging into the application the action filter is getting called. why this is so ?
We want to use the filter on view and controller side. So, basically we are looking like this
[Route("Course/Info")]
[SecurityFilter(PermissionName = "some permission")]
public ActionResult CourseDetails()
{
return View();
}
public class SecurityFilter : ActionFilterAttribute
{
public string PermissionName { get; set; }
public SecurityFilter(SessionServices _session)
{
session = _session;
}
public SecurityFilter()
{
//Unable able to remove the default constructor
// because of compilation error while using the
// attribute in my controller
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission") != null)
{
List<OrganizationUserRolePermission> permissionList = session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission");
checkPermission = permissionList.Any(m => m.PermissionName == PermissionName);
if(!checkPermission)
{
// Redirect to unauthorized access page/error page
}
}
base.OnActionExecuting(context);
}
}
and whatever the permission we passed to the filter will check whether user has the permission OR not. Also, we are trying to inject session service into filter, but getting session null.
I'm not sure about your use case to pass the SessionServices
instance to filter attribute constructor but this is not possible as any
argument to Attribute invocation should be a compile-time constant
value.
Reference
Attribute parameters are restricted to constant values of the following types:
- Simple types (bool, byte, char, short, int, long, float, and double)
- string
- System.Type
- enums
- object (The argument to an attribute parameter of type object must be
a constant value of one of the above types.)
- One-dimensional arrays of any of the above types
Rather you could retrieve the stored session data inside the OnActionExecuting method directly to check the needed permissions.
Ideally Authorize attribute would be more appropriate in your case to check the user permissions to allow access to any view. I believe ActionFilter might be more suitable in case of any logging before/after the action execution.
Regarding the below
So, while logging into the application the action filter is getting called. why this is so ?
Please check the Filter Registration in your application code. Ideally if the filter is applied to any specific action (e.g. CourseDetails in your case) then it will be called only on that particular action execution.
Alternatively please include the Login action in your question so that we could check for the problem if any.
I hope this would help find a solution in your case!
Hello :) I am building an MVC5/EF6 system that has stores information about students with a number of user types . When the user logs in certain information about the user is stored in Session; UserID, UserTypeID etc. Different users have different privileges, and I often need to get the user information from Session within my ActionResult methods in each controller:
private Student GetCurrentStudentInfo()
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
I have this method at the bottom of my controllers, which I call from each method depending on when I need this information. It gets the userID from the current logged in user and returns the profile information. I would like to be able to either:
Make this method available to all my controllers
Or create a class variable that I can use at the top of my controller, which would return this info:
public class RegistrationWizardController : Controller
{
private readonly DefaultContext _db = new DefaultContext();
private UserInfo _userInfo = new UserInfo();
}
I am very new to MVC and coding in general, so any help/opinions/other suggestions are welcome. Thanks!
You have a couple of options.
The first (and easier) of the two is to make all of your controllers inherit from a common base controller. To do this, make a base controller that extends from the default controller:
public class BaseController : Controller
{
protected Student GetCurrentStudentInfo() //protected so we can access this method from derived classes
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
}
Now, change your controllers to inherit the base controller you just created:
public class RegistrationWizardController : BaseController
{
public ActionResult AnAction()
{
var student = this.GetCurrentStudentInfo(); //calls the method inherited from BaseController
}
}
The other option you have is to use Dependency Injection. This is a bit more complicated, and much more abstract than the previous method. There are a bunch of Dependency Injection frameworks, my favorite is Ninject (http://www.ninject.org/). This would probably be closer to the "Industry Standard" of doing something like this, and I would encourage you to at least look into it, but I think a sample would be a little out of scope for this question (do some side reading first). If you do decide to implement it and get stuck, post another question.
How can I access a ServiceStack.net session in my validation code?
public class UserSettingsValidator : AbstractValidator<UserSettingsRequest>
{
public UserSettingsValidator()
{
RuleFor(x => x.UserId)
.SetValidator(new PositiveIntegerValidator())
.SetValidator(new UserAccessValidator(session.UserId)); //<-- I need to pass the UserID from the session here
}
}
In the Service Implementation I just do:
var session = base.SessionAs<UserSession>();
but this does not work for my abstract validator.
Thanks!
Edit: this is version 3.9.71.0
I assume you are just using the ValidationFeature plugin, as most do. If that's the case, then I don't think it is possible. Ultimately the ValidationFeature is a plugin which uses a RequestFilter.
I wanted to do something similar before too, then realised it wasn't possible.
The RequestFilter is run before the ServiceRunner. See the order of operations guide here.
What this means to you is your populated request DTO reaches your service, and the validation feature's request filter will try validate your request, before it has even created the ServiceRunner.
The ServiceRunner is where an instance of your service class becomes active. It is your service class instance that will be injected with your UserSession object.
So effectively you can't do any validation that relies on the session at this point.
Overcomplicated ?:
It is possible to do validation in your service method, and you could create a custom object that would allow you pass the session along with the object you want to validate. (See next section). But I would ask yourself, are you overcomplicating your validation?
For a simple check of the request UserId matching the session's UserId, presumably you are doing this so the user can only make changes to their own records; Why not check in the service's action method and throw an Exception? I am guessing people shouldn't be changing this Id, so it's not so much a validation issue, but more a security exception. But like I say, maybe your scenario is different.
public class SomeService : Service
{
public object Post(UserSettingsRequest request) // Match to your own request
{
if(request.UserId != Session.UserId)
throw new Exception("Invalid UserId");
}
}
Validation in the Service Action:
You should read up on using Fluent Validators. You can call the custom validator yourself in your service method.
// This class allows you to add pass in your session and your object
public class WithSession<T>
{
public UserSession Session { get; set; }
public T Object { get; set; }
}
public interface IUserAccessValidator
{
bool ValidUser(UserSession session);
}
public class UserAccessValidator : IUserAccessValidator
{
public bool ValidUser(UserSession session)
{
// Your validation logic here
// session.UserId
return true;
}
}
public class UserSettingsValidator : AbstractValidator<WithSession<UserSettingsRequest>>
{
public IUserAccessValidator UserAccessValidator { get; set; }
public UserSettingsValidator()
{
// Notice check now uses .Object to access the object within
RuleFor(x => x.Object.UserId)
.SetValidator(new PositiveIntegerValidator());
// Custom User Access Validator check, passing the session
RuleFor(x => x.Session).Must(x => UserAccessValidator.ValidUser(x));
}
}
Then to actually use the validator in your service:
public class SomeService : Service
{
// Validator with be injected, you need to registered it in the IoC container.
public IValidator<WithSession<UserSettingsRequest>> { get; set; }
public object Post(UserSettingsRequest request) // Match to your own request
{
// Combine the request with the current session instance
var requestWithSession = new WithSession<UserSettingsRequest> {
Session = this.Session,
Object = request
};
// Validate the request
ValidationResult result = this.Validator.Validate(requestWithSession);
if(!result.IsValid)
{
throw result.ToException();
}
// Request is valid
// ... more logic here
return result;
}
}
I hope this helps. Note: code is untested
It appears that after reading from a bunch of people experiencing similar problems, then many hours of playing with several solutions based on the SS4 Cookbook etc, this is a problem that is already solved:
https://forums.servicestack.net/t/blaz-miheljak-355-feb-3-2015/176/2
Implement the IRequiresRequest interface on your validator, and voila.
When a user signs in to my website, I want cache some data like email, confirmation status, mobile confirmation status, etc. Because I don't want fetch this data in each page request. The requirement is that the user must confirm email and mobile before do anything.
I am using code like this:
public static class CachedData
{
public static bool IsEmailConfirmed
{
get
{
if (HttpContext.Current.Session["IsEmailConfirmed"] == null)
Initialize();
return Convert.ToBoolean(HttpContext.Current.Session["IsEmailConfirmed"]);
}
set
{
HttpContext.Current.Session["IsEmailConfirmed"] = value;
}
}
public static bool IsMobileConfirmed
{
get
{
if (HttpContext.Current.Session["IsMobileConfirmed"] == null)
Initialize();
return Convert.ToBoolean(HttpContext.Current.Session["IsMobileConfirmed"]);
}
set
{
HttpContext.Current.Session["IsMobileConfirmed"] = value;
}
}
public static void Initialize()
{
UserAccount currentUser = UserAccount.GetUser();
if (currentUser == null)
return;
IsEmailConfirmed = currentUser.EmailConfirmed;
IsMobileConfirmed = currentUser.MobileConfirmed;
}
}
I have PageBase class that all page classes drive from it. I am using class CachedData in PageBase class:
public class PageBase : Page
{
protected override void OnInit(EventArgs e)
{
if (authentication.Required && User.Identity.IsAuthenticated && !IsPostBack)
{
if (CachedData.HasProfile && (!CachedData.IsEmailConfirmed || !CachedData.IsMobileConfirmed) && !Request.Url.AbsolutePath.ToLower().EndsWith("settings.aspx"))
Response.Redirect("/settings-page", true);
}
}
}
May be it is strange, but this code, sometimes work wrong and redirect to setting page for user confirmed email and mobile.
Is there any better solution.
I think, if this is your logic, you should create an object UserInfo. Something like this:
public class UserInfo
{
public string Name {get; set; }
public bool IsEmailConfirmed {get; set; }
public bool IsMobileConfirmed {get; set; }
....
}
Then set this object into session. Now! when any operation on user record are performed in your BLL, you should re-populate new Instance of UserInfo and replace old one in the session. This way your user info will be up to day and will always work.
But your problem may coming from the fact that you use a web farm and your sessions are not synchronized. You need to use a sticky session so each request from the unique user is processed on the same server. Right now there is thing called App Fabric. It is caching on steroids. It can find an item in cache on another server.
You should not store different fields of your object in the different session names.
If you do need to use sessions, you can store the whole user object in your session.
The choice where to store the data depands on the requirements (including how critical are the data and the requirements for such the things as IIS reset) and what and why you really have to store.
Depending on the answers you could store your data either in a session or in a viewstate or in cache or in application.
You can also look at the cache because it provides some nice features like automatic update, triggering, etc.
I recently starded developing for MVC 3 but have experience in both C# and ASP.NET since earlier. So i'll start with what i'm trying to accomplish. I've developed a small site for hosting articles. I've implemented SQLServer based membership managament to the site. Now i want to create a credentials system that restricts and allows the right users to create, delete and update articles. There is one simple solution to this and that is to do it like this:
[Authorize(Roles="Admin")]
public ActionResult UpdateArticle(ArticleModel model, int articleid)
{
return View();
}
Now this is really simple. I simply say that only members that are in the role "Admin" are allowed to update an article. But that's just to static. So i created a credentials table in my database that in the end tells me that "Article 5 can be edited by roles 1,2,3 & 4 and by users A, b & C". So far so good. But how would i implement that with the Authorize solution?
I would like to do something like this:
[Authorize(getAuthorizedusers("update",this.articleid))]
where getAuthorizedusers returns which users and roles are authorized to update the article with the articleid that was passed to it.
So I have (at least) two problems here:
-Getting the Authorize method to accept multiple users and roles.
-Passing the supplied articleid, that was sent to the UpdateArticle method, to the getAuthorizedusers method.
You can create your own custom attribute that inherits from AuthorizeAttribute and override the OnAuthorize method to do what you need.
This should get you started:
public class ArticleAuthorizeAttribute : AuthorizeAttribute
{
public enum ArticleAction
{
Read,
Create,
Update,
Delete
}
public ArticleAction Action { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//do custom authorizization using Action and getting ArticleID
//from filterContext.HttpContext.Request.QueryString or
//filterContext.HttpContext.Request.Form
}
}
The usage would look like this:
[ArticleAuthorize(Action=ArticleAuthorizeAttribute.ArticleAction.Update)]
Edit: After looking into this a bit more, it looks like you can't pass this.articleID in to the attribute. However, you do have access to the parameters from filterContext.HttpContext.Request through the QueryString property or the Form property, depending on how you are passing the values. I have updated the code sample appropriately.
A more complete example can be found here
To check for authorization using user role and user list you would do something like this:
var allowedUsers = new List<string>();
//populate allowedUsers from DB
If (User.IsInRole("Update") || allowedUsers.Contains(User.Identity.Name))
{
//authorized
}
Alternatively, you can do both checks against the DB directly in a single method to keep from making two calls.
Here's a much easier way to accomplish the same thing:
[Authorize]
public ActionResult UpdateArticle(ArticleModel model, int articleid)
{
// if current user is an article editor
return View();
// else
return View("Error");
}
I got it working as I wanted when I overrode the AuthorizeCore method and authorizes the way I want to.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
if ((_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) && (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)))
{
return false;
}
return true;
}