Global Variable for ASP.Net MasterPage - asp.net

I am converting a webapp from a frameset layout to Masterpage layout. Each of the pages within the frameset use a common custom class to aquire the logged in user, this custom class inherits from Page.
I thought that an interface would be the proper way to convert this however I was really wrong. What is the best and most efficient means to store a global variable or class so that each content page for the Masterpage can reference the logged in user class?

Make it a simple static class with static method that will return logged user class, there is no need to tie this code with ASP.NET page. If getting the logged on user requires some time vise expensive operations (like DB query for example) store logged user class instance in the session. Something like this :
public static class Users
{
public static User GetLoggedUser()
{
if (HttpContext.Current.Session["LoggedUser"] != null)
{
return (User)HttpContext.Current.Session["LoggedUser"];
}
else
{
User usr = GetLoggedUserSomehow(); // here goes your current code to get user
HttpContext.Current.Session["LoggedUser"] = usr;
return usr;
}
}
}

Related

Persisting Data

I am building an MVC 4 intranet site that I would like to have persistent data with in a footer. I'm going to hold a few basic stats that are unique to each user. I want this data to persist across all pages during a user's visit to the site.
What is the best best way to persist this data? A cookie? It's just a few non-senstive bits of information that will be pulled from AD. I was thinking hidden fields but that seems a bit cumbersome.
Thanks for the insight!
While you could use sessions for this task, I would caution against. Sessions should be avoided as much as possible.
You can use a child action to build the footer, using AD to get the relevant details, and then render this in your layout. If you're concerned about AD being hit every request to render the footer, then just cache the result.
public class FooController : Controller
{
...
[ChildActionOnly]
public ActionResult Footer()
{
// query AD and stuff the info in a view model
return PartialView("_Footer", model);
}
}
Then, in your layout:
#Html.Action("Footer", "Foo")
Of course, you'll need to move your current footer view code into a partial view, named _Footer.cshtml based on this example.
To cache, just prefix the child action with:
[OutputCache(Duration = 3600, VaryByCustom = "User")]
Duration is in seconds, so the above would cache for an hour. And you'll need the following method added in Global.asax:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "User")
{
if (context.Request.IsAuthenticated)
{
return context.User.Identity.Name;
}
return null;
}
return base.GetVaryByCustomString(context, custom);
}

Display authenticated and anonymous content in a view

I'm newbie in web development.
Consider site that user can be logged in (e.f Facebook log-in)
Assuming I know if a user is logged in or not (I'm on my way to find how ;) - is it possible that on the same view (.cshtml) - part of the elements will be hidden if user anonymous or will be revealed if user is logged-in? you know - something like nice attributes or conditions (in short - to put the logic on the same view and not to manage two .cshtml)
I'm personally not a fan of having if statements in my view's as they can easily begin to get cluttered, especially if you are using roles.
This is the way I prefer to do it to avoid that.
Create an html helper containing the logic like this abstracted away:
namespace System.Web.Mvc
{
public static class HtmlHelperExtensions
{
public static MvcHtmlString UserMessage(this HtmlHelper htmlHelper)
{
string welcomeFormat = "Welcome, {0}";
var isAuthenticated = htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated;
var name = htmlHelper.ViewContext.HttpContext.User.Identity.Name;
var message = isAuthenticated ? string.Format(welcomeFormat, name) : string.Format(welcomeFormat, "anonymous!");
return new MvcHtmlString(message);
}
}
}
Call this method within my view like this:
<p>#Html.UserMessage()</p>
If you are new to web development the code for the Helper extension might look a bit overwhelming but you only end up writing that once and the code you use to call it elsewhere is a lot simpler and re-usable.
Here is an article about Html helpers for more info:
http://www.codeproject.com/Articles/649394/ASP-NET-MVC-Custom-HTML-Helpers-Csharp
Update
Forgot to mention this technique too, which again avoids the if statements.
Create two partial views say _auth.cshtml & _unauth.cshtml.
Create an action that checks if the user is authenticated & returns the relevant partial i.e
public ActionResult FooContent()
{
if (User.Identity.IsAuthenticated)
{
return PartialView("_auth");
}
else
{
return PartialView("_unauth");
}
}
Then call the action from within your view like this:
#Url.Action("FooContent", "Contoller");
This can also be used to check roles and return different partials.
Yes, views can have logic.
Here's an example of code that displays different content to the user depending on if they are logged in or not.
#if (User.Identity.IsAuthenticated)
{
<p>Welcome, #User.Identity.Name!</p>
}
else
{
<p>Welcome, anonymous!</p>
}

Asp.net MVC Block a user from accessing entire application on certain conditions

I have a website that has for examples pages A, B, C, D,.... I want that the user can only access page A once he has registered. To access the remaining pages the user needs to complete some joining formalities which include making a payment among others.
Now the simple way would be where I add a check in all my pages currently completed to make sure that the pages B, C, D... requested are displayed only if the user has completed all joining formalities. This will need me to re work all those existing pages all over again. Also, all future pages developed will need to remember to add that check.
To come around this I used a custom authorize attribute class that inherits from AuthorizeAttribute and made a check in the overridden OnAuthorization method there as in the snippet below
else if (!SessionCache.FormalitiesCompleted)
{
//force completion of formalities
if (!( string.Equals(filterContext.ActionDescriptor.ActionName, "Index", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Options", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Index_B", StringComparison.InvariantCultureIgnoreCase)
|| string.Equals(filterContext.ActionDescriptor.ActionName, "Index_C", StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, "Formalities", StringComparison.InvariantCultureIgnoreCase)))
{
string _action = filterContext.ActionDescriptor.ActionName;
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Formalities", action = _action, area = "FormalitiesArea" }));
}
}
As you can see there are too many OR conditions in the IF. I suspect this will increase as the complexity of the formality increases.
Is there a better way to enforce the user doesn't visit any other page on the site unless the formalities are completed?
Thanks for your time.
You probably should look at the [Authorize attribute] http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx If you decorate an action with that attribute only logged in users can access it and others will be redirected to the login page.
For Paid/Registered users, I would inherit from this and make a PaidUserAuthorize attribute:
public class PaidUserAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if(filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result = new RedirectResult("/PaymentPage");
}
}
}
Therefore decorating an action with
[PaidUserAuthorize(Roles = "PaidUser", "Super User")]
will redirect unpaid users to the payment page.
After a user pays - add them to the PaidUser role. So when they go to the action again they will be allowed access.
You can create an Actin Filter and apply it to the Actions or Classes that you want to restrict access too. In you actions filter, you'll want to override the OnActionExecuting method and implement your checking logic in there. You can allow or deny access to those views based on whatever you want to do
Check this link out for a sample
What about using the [Authorize] annotation in your Controllers? By creating your own AuthorizeAttributes you should be able to customise the authorisation per action based on whatever MembershipProvider you choose to use or create.

ASP.NET MVC3 Role and Permission Management -> With Runtime Permission Assignment

ASP.NET MVC allows users the ability to assign permissions to functionality (i.e. Actions) at Design Time like so.
[Authorize(Roles = "Administrator,ContentEditor")]
public ActionResult Foo()
{
return View();
}
To actually check the permission, one might use the following statement in a (Razor) view:
#if (User.IsInRole("ContentEditor"))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
The problem with this approach is that all permissions must be set up and assigned as attributes at design time. (Attributes are compiled in with the DLL so I am presently aware of no mechanism to apply attributes (to allow additional permissions) such as [Authorize(Roles = "Administrator,ContentEditor")] at runtime.
In our use case, the client needs to be able to change what users have what permissions after deployment.
For example, the client may wish to allow a user in the ContentEditor role to edit some content of a particular type. Perhaps a user was not allowed to edit lookup table values, but now the client wants to allow this without granting the user all the permissions in the next higher role. Instead, the client simply wants to modify the permissions available to the user's current role.
What options are strategies are available to allow permissions on MVC Controllers/Views/Actions to be defined outside of attributes (as in a database) and evaluated and applied at runtime?
If possible, we would very much like to stick as closely as we can to the ASP.NET Membership and Role Provider functionality so that we can continue to leverage the other benefits it provides.
Thank you in advance for any ideas or insights.
What options are strategies are available to allow permissions on MVC
Controllers/Views/Actions to be defined outside of attributes (as in a
database) and evaluated and applied at runtime?
A custom Authorize attribute is one possibility to achieve this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
Roles = ... go ahead and fetch those roles dynamically from wherever they are stored
return base.AuthorizeCore(httpContext);
}
}
and then:
[MyAuthorize]
public ActionResult Foo()
{
return View();
}
As I'm lazy I couldn't be bothered rolling my own attribute and used FluentSecurity for this. In addition to the ability to apply rules at run time it allows a custom way to check role membership. In my case I have a configuration file setting for each role, and then I implement something like the following;
// Map application roles to configuration settings
private static readonly Dictionary<ApplicationRole, string>
RoleToConfigurationMapper = new Dictionary<ApplicationRole, string>
{
{ ApplicationRole.ExceptionLogViewer, "ExceptionLogViewerGroups" }
};
the application roles are then applied like so
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() =>
HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(() =>
GetApplicationRolesForPrincipal(HttpContext.Current.User));
configuration.ForAllControllers().DenyAnonymousAccess();
configuration.For<Areas.Administration.Controllers.LogViewerController>()
.RequireRole(ApplicationRole.ExceptionLogViewer);
});
filters.Add(new HandleSecurityAttribute());
and then the check is performed by
public static object[] GetApplicationRolesForPrincipal(IPrincipal principal)
{
if (principal == null)
{
return new object[0];
}
List<object> roles = new List<object>();
foreach (KeyValuePair<ApplicationRole, string> configurationMap in
RoleToConfigurationMapper)
{
string mappedRoles = (string)Properties.Settings.Default[configurationMap.Value];
if (string.IsNullOrEmpty(mappedRoles))
{
continue;
}
string[] individualRoles = mappedRoles.Split(',');
foreach (string indvidualRole in individualRoles)
{
if (!roles.Contains(configurationMap.Key) && principal.IsInRole(indvidualRole))
{
roles.Add(configurationMap.Key);
if (!roles.Contains(ApplicationRole.AnyAdministrationFunction))
{
roles.Add(ApplicationRole.AnyAdministrationFunction);
}
}
}
}
return roles.ToArray();
}
You could of course pull roles from a database. The nice thing about this is that I can apply different rules during development, plus someone has already done the hard work for me!
You could also consider doing task/activity based security and dynamically assign permission to perform those tasks to different groups
http://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-authorization-checks-do-activity-based-checks/
You would need to mangle the provider a little bit to work with this but it is possible to stay inline with the .net authorisation
http://www.lhotka.net/weblog/PermissionbasedAuthorizationVsRolebasedAuthorization.aspx
If you need to do Method or Controller based authorization (deny access to the whole method or controller) then you can override OnAuthorization in the controller base and do your ouwn authorization. You can then build a table to lookup what permissions are assigned to that controller/method and go from there.
You can also do a custom global filter, which is very similar.
Another option, using your second approach, is to say something like this:
#if (User.IsInRole(Model.MethodRoles))
{
<div>This will be visible only to users in the ContentEditor role.</div>
}
And then in your controller populate MethodRoles with the roles assigned to that method.

How can ASP.NET MVC enhance security compared to a webform?

Since we have separation of layers it should be easier to isolate each layer by security. How does MVC in ASP.NET exploit this to more easily secure a website compared to using a webform?
By security I do not only mean authorization but also anti-hacking security.
1) Decorate Controller/Action by [Authorize] attribute (optionnaly with list of roles that allowed).
Example:
[Authorize("Manager")]
public class MyController:Controller
{
//Each action available only for authorized user
[Authorize(Roles = "Admin;Customer")]
public ActionResult MyAction()
{
//This action is available to the user that have a Manager role and one of next roles: Admin, Customer.
}
public ActionResult AnotherAction()
{
// This action is available for managers only.
}
}
2) Create area at your MVC project, create base controller, that decorated by authorize attribute, and make all controllers at area inherited from decorated controller - that's way you can easy implement admin area for website, for example.
Example:
[Authorize(Roles = "Admin")]
public class BaseController: Controller
{
}
public class My2Controller: BaseController
{
public ActionResult DoSomething()
{
//This action is available only for Admin.
}
}
If you need to protect site from HTML/script injection (user input ... at text field 'surname', that will collect data from admin page 'all site users' and post to his site when you open your page) - you can use next methods.
do nothing - by default MVC validates input, and don't process requests with HTML tags and returns error
use validation - for many kinds of data (names, phones, dates) user should not have possibility to use '<' and '>'. but you don't want user see error page if makes accidental mistake. Than enable client validation.
Use attribute [ValidateInput(false)] and encode incoming data - but user can't input HTML if he really need it.
If you really need user can format his message - use HTML filters for giving possibility input tags from list of permitted. As example: http://htmlagilitypack.codeplex.com/
Provide your own formatting dialect that will be safe and will not use HTML tags
If you need to protect from cross site fake form posting - use [ValidateAntiForgeryToken] attribute with #Html.AntiForgeryToken(); inside your <form> tag. This method generates hidden input with random value and cookie with known key and same value, and attribute check if form contains value, cookie contains value and form value equals to cookies value.
You can create your own security rules, using a bunch of attribute and hidden input on form: for example to set form time-outs.
You can put authorize attributed on each function call with role requirements.
One way users can hack your site is through "Cross-Site Request Forgery" and you can help prevent it by using AntiForgeryToken().
// Your View
#using (Html.BeginForm("Register", "Account"))
{
// Your Form Fields
#Html.AntiForgeryToken("someSalt")
}
// Your Controller
[HttpPost, ValidateAntiForgeryToken(Salt = "someSalt")]
public ActionResult Register(MyViewModel viewModel)
{
// Your Code
}

Resources