Handling Session_Start in an Orchard CMS module - c#

A little background:
We need to develop a custom Orchard module for a client that will catch the external URL referrer (if any) and store it in a session variable for later use, e.g. submitting a query for one of their products.
My naive solution was to suggest we record the URL referrer on Session_Start because it is a reliable way of knowing how the user got to our site. The problem is that the client does not want us touching the global.asax.cs file. It has to be done via a custom module. This is non-negotiable.
So my question is this: how can I reliably retrieve and store the UrlReferrer information when a new session starts using an Orchard module?
Or alternatively, is there some other way I can hook into the page lifecycle and maybe check whether or not the previous page was an external referrer?
My most important concern here is I need to know whether someone clicked on a sponsored link and I need to find that out in a module, not global.asax.cs. I am not dead set on any particular tracking method, as long as it is possible in the Orchard framework given the limitations imposed on me.
FYI: Orchard version is 1.8+

You can do this from a filter. I implemented this a while ago in my commerce module, to enable giving discounts or attributions for conversions from partner sites, typically. You can see the source code for my filter here: https://github.com/bleroy/Nwazet.Commerce/blob/master/Filters/ReferrerFilter.cs

What I would do, is create a custom module and in there a custom controller:
public class ReferrerController : Controller {
public ActionResult Index(string referrer) {
if (Session["Referrer"] != null) {
// do nothing, already used as entry point in the current session
} else {
// handle referrer, probably also some timestamp or hash
Session["Referrer"] = referrer; // save in session
}
return RedirectToRoute("~/"); // redirect to home
}
}
Obviously, also create a route for this. Then from the external referrers, go to this route where the referrers will be handled. (http://example.com/referrer?referrer=somereferrer)

Related

MVC configurable Authorization filter

I want to make the [MyAuthorize(Role="R1")] attribute so that
"R1" can be made configurable instead of hardcoding on Controller / Action.
The usual approach of creating a [MyAuthorize(Role="R1")] seems to be
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private readonly string[] _allowedRoles;
public MyAuthorizeAttribute(params string[] roles)
{
this._allowedRoles = roles;
}
protected override bool OnAuthorization(AuthorizationContext
authorizationContext)
{
bool authorize = false;
// Compare current user's Roles with "R1" to figure out if the
// Action / Controller can be executed
return authorize;
}
}
But what if Roles like "R1" are subject to change at any time ?
i.e., being "R1" one day and being called "AssistantManager" another day.
The application will have to be re-coded to handle this.
I thought of creating a custom [OnAuthorize] attribute that reads
(Action/Controller, Role) as key value pairs from the web.config.
Eg:--
<add key="Controller1" value="Role1" />
<add key="Action2" value="Role2" />
and in the attribute..
protected override bool OnAuthorization(AuthorizationContext
authorizationContext)
{
bool authorize = false;
// 1. Read all key values
// 2. determine Action / Controller the user is trying to go
// 3. Compare user's roles with those for Action / Controller
return authorize;
}
I am aware of the limitations of <location .... /> in MVC
as per https://stackoverflow.com/a/11765196/807246
and I'm not suggesting that, even though I'm reading from web.config
But what if we read (..and store in session??) all the authorization related configuration when the application first loads up?
Any changes like "R1" -> "AssistantManager" ;; "R2" -> "Manager" should just require a restart of the application, instead of having to make code changes in the controller / action.
I want to know if this is a valid approach or if there are security risks, even with this, and any better alternatives.
Ad 1. You read the setting using the configuration API, e.g. if this is the regular MVC you have ConfigurationManager.AppSettings to peek into app settings section of the web.config
Ad 2. You don't determine anything or rather, you seem to misunderstood the linked post. What you do is you put the Authorize over the controller (action) you want to secure and the OnAuthorization is fired when the controller / action is executed. If you really want, you can peek into the authorization context passed as the argument, the controller and action are available in the route data.
Ad 3. This is the easiest part, the currently logged user (or an anonymous user if the user is not yet authenticated) is passed in the authorizationContext.HttpContext.User property as the IPrincipal so you can even call its IsInRole method.
But what if we read (..and store in session??) all the authorization related configuration when the application first loads up
You really don't have to. Even if you read it from the configuration upon every request, the configuration is already preloaded upon every restart of the application, you don't really slow anything down much then with ConfigurationManager.AppSettings.
Any changes like "R1" -> "AssistantManager" ;; "R2" -> "Manager" should just require a restart of the application, instead of having to make code changes in the controller / action.
If you store it in the configuration file and modifying it triggers the restart of the app pool, you don't make any changes in the code.
I want to know if this is a valid approach or if there are security risks, even with this, and any better alternatives.
There are risks, someone who can access your app server can possibly reconfigure your app. Note however, that such someone could do any other harm as well, e.g. decompile, modify, recompile and reupload your app. Or even replace it with something completely else.
As for alternatives, it's completely impossible to come up with anything better if the criteria of what's better are vague. If something is possibly better we'd have to know what better stands for.
In other, simple words, this looks fine.

Global redirect back to front page if claim is empty or does not exist (NOT identity verification)

Just to be clear: I am NOT talking about claims-based identity validation.
I am building an app in which I make fine use of Identity 2.2 to provide validation. It is sufficient for my needs.
My problem is that once a user logs in, only the first page is widely accessible without storing additional information in the user’s “session”. In particular, when the user clicks on a major internal item (for sake of convenience, let’s call this a “customer module”, the Guid for that customer is stored in a claim held by the user. That way, the user can move from page to page and still have that same customer’s data brought up on every page regardless of what chunk of data the page was meant to display. This claim is only refreshed with something different when they return to the main page and click on another customer.
For security’s sake I would like to ensure that if a claim gets accidentally dropped or set to empty, the user gets shunted back to the main page regardless of where they are in the system, and preferably without having to put code in each and every page action of every controller.
Suggestions? Or am I completely wrong by making use of claims? Because it’s still early enough in the project to make a u-turn if the advantages of a different method are compelling enough.
EDIT:
Just to let people know my solution: Because only one group of people will be accessing this application (the users that interact with companies, this app is to record the interactions and “company information”), I decided to use a base controller. The users would be able to log on and view lists of companies without coming across any page that derived from BaseController, but once they chose a Company to work with, they needed to have Claims set to be able to maintain page-by-page contact with this company’s information. This information would be reset only when they chose a different company, but since there was always a chance that a claim could be disabled, I needed something to automagically redirect them back to the list of companies to re-set the claims. A BaseController that was employed by only those pages where information specific to one company would be displayed was the perfect solution.
A base controller is simple. Just create a controller called BaseController and you’re off to the races. Change any controller that needs to work with this base controller such that they are public class YourOtherController : BaseController.
I initially tried to make an Initialize method to handle everything, but ran into a rather big problem: I was unable to successfully both access and write to my Claims. As in, I was able to either read my claims but not make use of my ClaimWriter extension, or I was able to make use of my ClaimWriter extension but be unable to read claims in the first place. Since Initialize is wayyyy too low in the stack to actually do both these things, I abandoned it and went for an OnActionExecuted method, which ended up being successful. My code ended up being this:
public class BaseController : Controller {
private ApplicationDbContext db = new ApplicationDbContext();
protected override void OnActionExecuted(ActionExecutedContext filterContext) {
base.OnActionExecuted(filterContext);
var principal = ClaimsPrincipal.Current.Identities.First();
var company = User.GetClaimValue("CWD-Company");
var prospect = User.GetClaimValue("CWD-Prospect");
if(string.IsNullOrEmpty(company)) {
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Redirect("/");
filterContext.HttpContext.Response.End();
}
if(!string.IsNullOrEmpty(company) && string.IsNullOrEmpty(prospect)) {
var id = new Guid(company);
var prospecting = db.Prospecting
.Where(x => x.CompanyId.Equals(id))
.Select(x => x.ProspectingId)
.ToList().SingleOrDefault();
if(prospecting.Equals(Guid.Empty)) { // null prospecting
User.AddUpdateClaim("CWD-Prospecting", "");
} else { // fill prospecting
User.AddUpdateClaim("CWD-Prospecting", Convert.ToString(prospecting));
}
}
}
}
I am probably going to change the if(prospecting.Equals(Guid.Empty) part of the Prospecting section to automagically create the first entry in the db (with all null values except for the ProspectingId and the CompanyId, of course), but this is what works for now.
That's a fine use of claims you describe, no need a u-turn. What you need is a MVC filter, authorisation filter. Something like this:
public class MyAuthorisationFilter : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var principal = HttpContext.Current.User as ClaimsPrincipal;
if(!principal.Claims.Any(c => c.Type == "My Claim Name"))
{
// user has no claim - do redirection
// you need to create 'AuthenticateAgain' route to your table of routes
// or you can do other means of redirection
filterContext.Result = new RedirectToRouteResult("AuthenticateAgain", new RouteValueDictionary());
}
}
}
Then you can add it globally in your filters configuration, but you'll have to exclude your authorisation page from this filter. Or apply on per controller basis - whenever this needs to happen.
This is very basic form of filter - a lot of checks are stripped out, but it gives a general direction how to proceed.
Update
This is a good article about Authorise attribute.
Here use of AllowAnonymous attribute is explained
The way you use it - depends on your scenario. In most cases when you only expose a login page to the world - it is sufficient to add this attribute as a global filter (see second link, part about RegisterGlobalFilters) and then sprinkle [AllowAnonymous] on top of controllers/actions which should be exposed without authentication.
Another approach is to have a base controller that has your attribute applied. And then all your controllers inherit from this base controller. This is more sufficient when global filter does not cut it: cases when you expose different pages to different users - think companies and customers. Your controllers for companies will inherit CompaniesBaseController that has [CompaniesAuthFilter] and customers will be inheriting from CustomersBaseController with [CustomersAuthFilter].

Need recommendation for global, targeted redirects on ASP.NET MVC site for multiple differing conditions

I'm working on an ASP.NET MVC application where administrators can add new users and flag them to complete additional information before they can use other features of the site. For example, we have a "ForcePasswordReset" bool, and a requirement to complete Security Questions. We're not using Active Directory for these users.
Ultimately this is the behavior I'd like to implement:
Direct any logged in user who is required to change password to the
ChangePassword view. And if that user clicks on other links, funnel
him back to the ChangePassword view.
Same scenario for users who must change their security questions.
Initially I placed the checks directly into a Login Controller ActionResult. But this only accounts for the Login action. An abbreviated code sample is below.
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// ...
// Does the user need to complete some missing information?
if (externalUser.IsSecurityQuestionInfoComplete == false)
return RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return RedirectToAction("ChangePassword", "MyInfo");
// Login was successful
return RedirectToLocal(returnUrl);
}
}
One problem with this approach is that there are other hyperlinks presented in those targeted views, where the user could navigate away from the targeted interface. So for example, a user directed to the ChangeSecurityQuestions view could just click away from it.
Logged-in users can change those settings at any time. I could create duplicate views for changing passwords and security questions that are fashioned just for this scenario, where the user is being forced to update these values. In the interest of staying DRY and reducing maintenance, I'd like to use the same views for both scenarios (users who just want to edit that info, and users who are being forced to edit that info). But, trying to stay DRY in this respect may be wrongheaded if the alternative is worse.
I started to write a method within a helper class to divert these users, trying something like this.
/// <summary>
/// Check scenarios where the ExternalUser needs to complete some missing information, and divert as necessary.
/// </summary>
public static void divertExternalUserAsRequired(Controller controller, ExternalUser externalUser)
{
if (externalUser.IsSecurityQuestionInfoComplete == false)
return controller.RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return controller.RedirectToAction("ChangePassword", "MyInfo");
}
But that RedirectToAction is inaccessible due to protection level. Moreover, that doesn't appear to be recommended (Is it possible to use RedirectToAction() inside a custom AuthorizeAttribute class?). And I don't like the idea of junking up my controllers by pasting this check all over the place.
UtilityHelpers.divertExternalUserAsRequired(this, externalUser);
What is the recommended approach to handling this scenario? I would perfer something that's more globally implemented, where the check can run when any relevant view loads.
Thanks for your help.
If I'm understanding your question correctly then you've got a few options available to you.
One option is to check the necessary conditions within Application_BeginRequest in your Global.asax.cs class. This method is called at the very beginning of every request and if the condition fails then you can load a different controller action like so:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (!externalUser.IsSecurityQuestionInfoComplete)
{
var routeData = new RouteData();
routeData.Values["action"] = "MyInfo";
routeData.Values["controller"] = "ChangeSecurityQuestions";
RequestContext requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
IController errorController = new ChangeSecurityQuestionsController();
errorController.Execute(requestContext);
requestContext.HttpContext.Response.End();
}
}
Another option available to you is to create and register a global action filter. As you mentioned in your question, you don't like the idea of littering your controllers with these condition checks. By registering a global action filter your controllers can remain completely unaware of the action filter being performed against it. All you need to do is register your action filter within Global.asax.cs like so:
protected void Application_Start()
{
...
GlobalFilters.Filters.Add(new SecurityQuestionCompleteFilter());
...
}
I hope this helps.

Is it acceptable to store user specific data in C# Runtime MemoryCache?

In my asp.net MVC app, each page contains a lot of data that changes very rarely, but is still user specific and should never be shared among other users.
I couldn't find a server side solution that handled a cache per-user, so my idea is to just use the 'standard' memorycache and use the user-ID as part of the key.
Is this acceptable? Am I missing some security risks?
Thanks
EDIT: (added details)
It is data that is originally stored in the database, for example, I have a custom-made dropdown list (which is retrieved using AJAX and returns a jsonresult) for product-categories. I want to be able to manually clear the cache, in case the user adds a category via settings and I need the retrieve a 'new' category list. To my knowledge I cannot manually clear the cache with OutputCache. I also have a scenario where I want to refresh certain cache item every minute (for some updates on the user's screen).
Annotate your action with OutputCache and for consistency make sure the action is for authorized access only.
[Authorized, OutputCache(VaryByCustom = "USER")]
public ActionResult SlowAction() { }
Then in Global.asax.cs override the 'VaryByCustom' handler
public override string GetVaryByCustomString(HttpContext context, string custom)
{
switch (custom)
{
case "USER":
return context.User.Identity.Name;
default:
return null;
}
}

ASP.NET 4.0 webforms routing

I have an existing site that I'd like to convert to use routing, and after reading Scott Guthrie's post here, I built a working sample that works for most circumstances. However, since not all of the pages on the existing site match a particular pattern, I'll need to check against a database to determine which route (destination .aspx page) to use.
For example, most pages are like this:
http://www.mysite.com/people/person.html
This is fine - I can easily route these to the view_person.aspx page because of the 'people' directory.
But some pages are like this:
http://www.mysite.com/category_page.html
http://www.mysite.com/product_page.html
This necessitates checking the database to see whether to route to the view_category.aspx page or the view_product.aspx page. And this is where I'm stuck. Do I create an IRouteHandler that checks the database and returns the route? Or is there a better way? The only code I've found that kind of fits is the answer to this question.
Thanks in advance.
If you don't mind doing so, the cleanest solution is to:
http://www.mysite.com/pages/category_page.html
In ASP.NET MVC, this situation would be handled a little differently, by specifying a default controller and action method on the root route.
Your route handler doesn't check the database. It sends all the requests to a handler .aspx script. It's that script that checks the database.
My register route looks like...
private static void RegisterRoutes()
{
Route currRoute = new Route("{resource}.axd/{*pathInfo}",
new StopRoutingHandler());
RouteTable.Routes.Add( "IgnoreHandlers", currRoute);
currRoute = new Route("{urlname}",
new EPCRouteHandler("~/Default.aspx"));
currRoute.Defaults = new RouteValueDictionary {{"urlname", "index.html"}};
RouteTable.Routes.Add( "Default", currRoute);
}
The custom handler, which shouldn't be needed in ASP.Net 4.0, simply passes the urlname parameter to the responding script as a URL variable.
Now how often the responding script checks the database depends on how often the data in the database is changed. You can easily cache paths and invalidate the cache when the data is suppose to have changed for instance.
For anyone stuck in the same situation, I ended up adapting the code from this answer to check against a database and return the proper ASPX page.

Categories