I'm creating a website in C# using MVC3. The website is a client that uses a web service. The web service uses a unique session id per user, which is given as the last parameter in each service call. You get the session id when you log in.
I'm able to get the session id and save it in a property in my user controller:
private string Sid
{
get
{
var sid = Session["Sid"];
return sid == null ? "" : sid.ToString();
}
set
{
Session["Sid"] = value;
}
}
In my other controllers I'm able to get the session id with a similar property (just without the setter), but when it asks the user controller to do something where it accesses its own property to get the id the session is null.
It seems like the sessions don't get transferred between the controllers, but I don't know how to do that. I would like to access the session from one central place, instead of having properties in each controller, but I can't figure out how to do it.
After three days of searching Google hasn't been able to give me the answer. Do you have any ideas?
Why not create a Base Controller which creates the Session variable in the OnActionExecuting method, then make all your other Controllers inherit from this Base Controller, like so:
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
Session["Sid"] = Session.SessionID;
base.OnActionExecuting(filterContext);
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
ViewBag.Name = Session["Sid"];
return View();
}
}
public class TestController : BaseController
{
public ActionResult Index()
{
ViewBag.Name = Session["Sid"];
return View();
}
}
Hope this helps!
Edit
As per your comment below here is, what I believe, you're looking for (?):
public class BaseController : Controller
{
public string Sid
{
get
{
var sid = Session["Sid"];
return sid == null ? "" : sid.ToString();
}
set
{
Session["Sid"] = value;
}
}
}
public class HomeController : BaseController
{
public ActionResult Index()
{
Sid = "2343432233aaaa";
ViewBag.Name = Session["Sid"];
return View();
}
}
public class TestController : BaseController
{
public ActionResult Index()
{
ViewBag.Name = Session["Sid"];
return View();
}
}
You can replace where I set Sid with the SessionId generated from your service
The behaviour you are describing is very unexpected; the Session is definitely the same, so there is something else causing this behaviour.
However, the "usual" way to deal with this is to create a custom SessionWrapper. The SessionWrapper is a static class with public properties, and most importantly, it's the single point where you will access the session - so all the controllers will use this class for writing to/reading from the session.
A very simple version would look something like this:
public static class SessionWrapper
{
public string Sid
{
get
{
var sid = Session["Sid"];
return sid == null ? "" : sid.ToString();
}
set
{
Session["Sid"] = value;
}
}
}
Related
I have a basic controller that extends from Controller, the class is working fine, but I figured that I am using a lot of times the code to get the current User from the database. So I figured I should make a constructor and move the code that I use in every function there.
Basically, what I wanted to do is have the parameters ready for any of the methods in my controller.
So, this is what I have right now (and it is working fine):
public class UsersController : Controller
{
private DBContext db = new DBContext();
public ActionResult Info()
{
User user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
return View(user);
}
public ActionResult Edit(int? id){
User user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
if(user.id == id){
return View(user);
}
}
}
But my idea was to create something like this:
public class UsersController : Controller
{
private DBContext db = new DBContext();
private User _user;
public UsersController()
{
_user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
}
public ActionResult Info()
{
return View(_user);
}
public ActionResult Edit(int? id){
if(_user.id == id){
return View(_user);
}
}
}
When I made these changes I get the following error:
Server Error in '/' Application.
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
I tried debugging and I found out that the problem is that my User is null when the constructor is called, so I am guessing, some other languages can call the parent constructor before adding or after adding their own customization, for example something like this:
public function __Construct($x){
$this->x = $x
parent::__construct();
}
or
public function __Construct($x){
parent::__construct();
$this->x = $x
}
I tried to do the same in my program, using base, but nothing seems to work and it always leads me to an error of some other nature.
I am not even sure that this is the right way to do it, because all I need is to have my User (Identity) created in the constructor
Sounds like the user isn't found, possibly because the user identity isn't populated on the thread's principal when the constructor for the controller is called.
My suggestion would be to avoid pulling the user data in the constructor and instead grab it when you need it. To avoid duplicating code, you can write a protected or private method (not an action method) to get it:
public class UsersController : Controller
{
private DBContext db = new DBContext();
private User GetCurrentUser()
{
return db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
}
public ActionResult Info()
{
var user = GetCurrentUser();
return View(user);
}
public ActionResult Edit(int? id){
var user = GetCurrentUser();
if(user.id == id){
return View(user);
}
}
}
As I mentioned in my question comment, inheritance is a poor choice here. Instead what you're attempting to do is give non-specific data to a View. A better choice is to use an ActionFilter.
We need a class to store User Information for the view to consume:
public class UserInfo
{
public bool HasUser { get; set; }
public User User { get; set; }
}
We need a place to store the data that is non-specific to views. I prefer using ViewData (because this route provides strongly typed data and an easy way to debug this storage location):
public static class ViewDataExtensions
{
private const string UserInfoKey ="_UserInfo";
public static void GetUserInfo(this ViewData viewData)
{
return viewData.ContainsKey(UserInfoKey)
? viewData[UserInfoKey] as UserInfo
: null;
}
public static UserInfo SetUserInfo(this ViewData viewData, UserInfo userInfo)
{
viewData[UserInfoKey];
}
}
Next we need a way to populate that information when needed
public class AddUserToViewDataFilterAttribute : ActionFilterAttribute
{
private DBContext db = new DBContext();
public void OnActionExecuting(ActionExecutingContext context)
{
var user = context.Controller.User;
var userInfo = new UserInfo
{
HasUser = !string.IsNullOrEmpty(User.Identity?.Name),
User = !string.IsNullOrEmpty(User.Identity?.Name)
? db.Users
.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault()
: null;
};
context.ControllerContext.ViewData.SetUserInfo(userInfo);
}
}
Populate it when needed:
public class MyController
{
public ActionResult DoesNotNeedUserInfo()
{
}
[AddUserToViewDataFilter]
public ActionResult NeedsUserInfo()
{
}
}
In the view:
#model <whatever>
#if (ViewData.GetUserInfo().HasUser) {
<div>#ViewData.GetUserInfo().User.Name</div>
}
I'm working on a WebApi project with Asp.Net.Identity v2 (with AspNetUser modified to use int IDs). I wanted to do a bit of refactoring by creating a base class for all my web api controllers. In particular, I wanted the MyBaseController class to encapsulate the logic of getting the current user id.
Previously, each action method in each controller called User.Identity.GetUserId<int>(). it was cumbersome but worked.
Now I decided to encapsulate that call either in the base class's constructor or in its property. However, it doesn't work: The Identity object I get is empty (IsAuthenticated = false, Name = null...), and GetUserId<int>() always returns 0.
Apparently it is only inside an action method that Identity is populated and GetUserId<int>() returns a correct result?
Here is basically what I get:
public class MyBaseController : ApiController
{
public int UserId => User.Identity.GetUserId<int>(); // **this always returns 0**
public MyBaseController() : base()
{ var userId = User.Identity.GetUserId<int>(); // **this is always 0** }
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
var userId = User.Identity.GetUserId<int>(); // **also 0**
}
public IHttpActionResult Get() {
var userId = User.Identity.GetuserId<int>(); // **the only place where it returns userId properly**
// some code ...
}
}
Is there a way to grab User.Identity other than in an action method to do something with it in a base class?
The project was initially created in VS 2013 with MVC 5 / WEbapi 2, now migrated to VS2015
You cannot access User.Identity before authorization. See the APIController life cycle here or on MSDN.
The information is not yet available in the constructor or in Initialize().
You can use a property in the base controller:
public abstract class MyBaseController : ApiController
{
public int UserID
{
get { return User.Identity.GetUserId<int>(); }
}
}
public class MyNormalController : MyBaseController
{
public ActionResult Index()
{
var userId = UserID; // put in variable to avoid multiple calls
// some code ...
}
}
I am trying to create another layer between my controller and my view so that I can pass different versions of a view to a user based on their "client ID" which would be the company to which they belong.
I have the following code:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
// set client
var client = new Client();
client.Id = Guid.NewGuid();
client.Name = "Foo";
// set user
var user = new User();
user.Id = Guid.NewGuid();
user.ClientId = client.Id;
user.Name = "Foo";
return ViewRenderer.RenderView("AddComplete", client);
}
}
My ViewRenderer class looks like this:
public static class ViewRenderer
{
public static ViewResult RenderView(string view, Guid clientId)
{
string viewName = GetViewForClient(view, clientId);
return Controller.View(view);
}
public static string GetViewForClient(string view, Guid clientId)
{
// todo: logic to return view specific to the company to which a user belongs...
}
}
The problem is, the line return Controller.View(view); in RenderView(string view, Guid clientId) gives me the error:
System.Web.Mvc.Controller.View()' is inaccessible due to its
protection level
I am interested to know how I can resolve this error or if there is a better way to do what I am trying to do, which is to display different versions of a view which are specific to the respective company to which a user belongs.
Edit: Another option I was kicking around in my head...
Is there a way to override the View() method such that I can prepend it with a directory name, for example, a user who belongs to "Acme Co." would call the same controller action as everyone else like View("MyView") but the method would actually be calling View("AcmeCo/MyView") however, I don't actually write that code in my controller, it's just derived from the user's client ID property.
You can just replace the view engine instead of adding another abstraction.
Write your own View engine (here is how to start off with a RazorViewEngine)
public class ByIdRazorViewEngine : RazorViewEngine
{
protected override IView CreateView(ControllerContext controllerContext,
string viewPath, string masterPath)
{
var id = // get something from controller context controllerContext
var newViewPath = CalculateViewPathFromId(id);
return base.CreateView(controllerContext, newViewPath, masterPath);
}
And register it in Global.asax.cs:
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ByIdRazorViewEngine());
}
The View() method is a protected member. You can only access it from within a derived type, such as your HomeController class. Plus you're trying to access it as a static method.
You can create a base Controller that exposes your specialized view logic. For the sake of illustration, I'm going to call it DynamicViewControllerBase
public class HomeController : DynamicViewControllerBase
{
//
// GET: /Home/
public ActionResult Index()
{
// set client
var client = new Client();
client.Id = Guid.NewGuid();
client.Name = "Foo";
// set user
var user = new User();
user.Id = Guid.NewGuid();
user.ClientId = client.Id;
user.Name = "Foo";
return RenderView("AddComplete", client);
}
}
public class DynamicViewControllerBase : Controller
{
protected ViewResult RenderView(string view, Guid clientId)
{
string viewName = GetViewForClient(view, clientId);
return View(view);
}
// Unless you plan to use methods and properties within
// the instance of `Controller`, you can leave this as
// a static method.
private static string GetViewForClient(string view, Guid clientId)
{
// todo: logic to return view...
}
}
If all you want to have is the company name prefixed to your controllers, apply the RoutePrefix attribute on to your controller.
Example:
[RoutePrefix(#"{company}")]
public partial class HomeController : Controller
{
}
And in your RouteConfig file,
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Make sure this line is added
routes.MapMvcAttributeRoutes();
}
Since your users must be authenticated to sign in to their accounts, once they've authenticated them selves you can either:
Store a cookie on your users machine with the name of their company
Make calls to your database on each request to retrieve this information
Make use of ViewData[]
etc..
Once you have the name of their company, you can construct the urls with that name.
Example:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model)
{
// ... authenticate user etc
// Redirect to
// foo.com/abc/home
return this.RedirectToAction("Index", "Home", new { company = "abc" });
}
If you're trying to work a way around this, I doubt you'll be able to as the web request first comes through a route, and the route decides which controller/action is executed, but to know the company name your action needs to execute to retrieve.
I have implemented my own custom Authorize attribute.
The attribute is applied both at the controller level and at the action level.
Here is an example of what I need to do:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
Currently if a user has the Administrator Role but not the AdvancedUsers role, he cannot execute "Administrative Task".
How can I change this behavior to perform a security check at the action level even if the user is not authorized at the controller level?
For the moment, the only solution I can think about is to implement 2 attributes: one for securing controllers, another for securing actions. Then I would play with the Order property to execute the one at the action level first.
However, I would prefer a solution with a single attribute if possible.
Use built-in [OverrideAuthorization]:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[OverrideAuthorization]
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
OverrideAuthorization Attribute is available for MVC 5 (at least) and up. Once you decorate the Action with it, also decorate with the new Role and that will take effect over the Controller level Role.
This should not be possible. Imagine the logic which MVC uses with the authorization filters.
When the controller is determined - check if there is an authorization filter that applies to that controller and execute it.
When the action is known - do the same for the action.
In all cases a fail in authorization would short-circuit the pipeline.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions.
When you mark an action method with the Authorize attribute, access to that action method is restricted to users who are both authenticated and authorized.
//[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
{
[ClaimsAuthorize(Roles ="Administrators", "Role2","Role3")]
public ActionResult AdministrativeTask()
{
return View();
}
}
OR you can override your authorization at controller level ,
Create a new OverrideAuthorizeAttribute attribute.
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
and you can use this attribute to override your controller level autorization.
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
[OverrideAuthorizeAttribute(Roles ="xxxx")] // This role will override controller
//level authorization
public ActionResult SomeOtherAction()
{
return View();
}
}
You need two authorization attributes - a base one with all authorization logic, and a second one, derived from the base attribute, that is only used to override the base attribute.
Example authorization attributes:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
protected bool _canOverride = true;
//...custom authorization code goes here.....
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Don't authorize if the override attribute exists
if (_canOverride && actionContext.ActionDescriptor.GetCustomAttributes<OverrideClaimsAuthorizeAttribute>().Any())
{
return;
}
base.OnAuthorization(actionContext);
}
}
public class OverrideClaimsAuthorizeAttribute : ClaimsAuthorizeAttribute
{
public OverrideClaimsAuthorizeAttribute ()
: base()
{
_canOverride = false;
}
}
In the base authorization attribute we are saying to go ahead and authorize as normal, as long as the OverrideClaimsAuthorizeAttribute doesn't exist. If the OverrideClaimsAuthorizeAttribute does exist, then only run the authorization on classes where _canOverride is false (ie the OverrideClaimsAuthorizeAttribute class itself).
Example usage:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
//Ignores the controller authorization and authorizes with Roles=Administrators
[OverrideClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
//Runs both the controller and action authorization, so authorizes with Roles=Administrators AND Roles=AdvancedUsers
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdvancedAdministrativeTask()
{
return View();
}
//authorizes with controller authorization: Roles=AdvancedUsers
public ActionResult SomeOtherAction()
{
return View();
}
}
Check this previous question. (check #AndyBrown answer, case 2)
For a simple way you might also try adding (
[AllowAnonymous]) to override the controller
[Authorize]
then add a new custom filter to check for your logic for this particular action. Or you can add the code that checks for the role just inside it.
How to enable Authentication on whole controller and disable only for certain action methods. I want authentication for all resources. If I write something like that:
[Authorize]
public class HomeController : BaseController
{
//This is public
[UnAuthorized]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}
Then anyone can access this resource. I need this because we have all resources are private and very few are public on our project. Do you have any ideas how to make it better way?
Organize your controllers accordingly. Have a base controller for all authenticated resources which you could annotate with the [Authorize] attribute and another one for public resources.
[Authorize]
public abstract BaseAuthenticatedController : Controller
{ }
public abstract BaseController : Controller
{ }
Based on solution which is found here I wrote the code that fixes exactly what I wanted.
Create custom authorization attribute base on AuthorizeAttribute and override method OnAuthorization:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext != null)
{
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(false);
if (attributes != null)
{
foreach (var attribute in attributes)
if (attribute is UnAuthorizedAttribute)
return;
}
}
base.OnAuthorization(filterContext);
}
I'm using a reflection here to recognize an action with UnAuthorized attribute. I don't know about performance issues in this case, but it solves the problem completely.
It's really strange that no one said about AllowAnonymous attribute which services for such situations:
[Authorize]
public class HomeController : BaseController
{
//This is public
[AllowAnonymous]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}