MVC 3 Page navigation security rules - model-view-controller

I have a MVC3 application which follows PRG pattern, I am looking for a best way to define navigation rules to my application. For example I have application with pages A, B, C and D. Lets say A is a logon page. After user logon successfully, user will be redirected to page B. Now I do not want let the user to type url of page C in the address bar and get access to page C (Page C should be accessible only after POST ing page B or from Page D back button) I have to setup similar rules for all other pages as well (lets say when user is in page D, should not allow them to get into Page B)
currently I have one option where I can check the #Request.UrlRefferer property to get the source of the every request and decide which page to redirect to. I am not sure this is a best solution.
Appreciate your feedbacks!!

Do not base your security on this. Use the [Authorize] attribute to define security. The UrlReferrer can easily be forged as well.
Why are you trying to limit this? If you have a business reason the user most go through a particular flow, then consider either a cookie, session, or database entry to note their current 'completion' status - IE some somewhat persistent method to determine this. You could also form a token based on say - a session id - that gets passed into each page. If the token exists and matches the user's current session , then load the page for them. Of course this could be forged if the user understands this - but if you are simply trying to ensure the proper flow then this is a way as well. The user would not get a link with the current session id in it until they hit the prior step.

If you don't want a particular page to be accessible via the URL, one option available is to make sure there's no way to access the page via a URL. To access the page, make a POST action that will return a view rather than a redirect. This would mean the view your POST action returns will be shown on a page with the URL of the previous page. For exameple:
Page A URL is /login and after logging in, the user is redirected to Page B. The URL is now /home. Page B sends a POST request and the contents of the page becomes Page C but the URL still remains as /home. The only way to view the contents of Page C would be to visit Page B and send a POST request.
This breaks the PRG pattern but that's one option.
There is one more alternative, store the current permissions of the user indicating which page they're allowed to enter and check if the user is authorized to view a page before executing the action. You could place the code in an ActionAttribute which you can apply to your action methods or entire controllers. If you'd like a more detailed explanation of this technique, leave me a comment and I'll write up another answer describing this technique in more detail.
Here's a quick proof-of-concept of the technique described above:
public class PermissionsNeeded : ActionFilterAttribute
{
string expectedPermission;
public PermissionsNeeded(string permission)
{
expectedPermission = permission;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentPermissions = filterContext.HttpContext.Session["CurrentPermissions"] as IEnumerable<string> ?? new List<string>();
// If user does NOT have permission to access the action method
if(!currentPermissions.Contains(expectedPermission)
{
throw new HttpException(403, "User is not authorized to view this page");
}
}
}
class YourController : Controller
{
[HttpPost]
public ActionResult PageB()
{
var currentPermissions = Session["CurrentPermissions"] ?? new List<string>();
currentPermissions.Add("PostedFromPageB");
Session["CurrentPermissions"] = currentPermissions;
return RedirectToAction("PageC");
}
[PermissionsNeeded("PostedFromPageB")
public ActionResult PageC()
{
return View();
}
}
Currently, the custom attribute will only accept one permission at a time which is a simply limitation to rectify. You'd be responsible for removing the permissions stored in the Session when you feel the user shouldn't have certain permissions anymore. I threw an HttpException that return a 403 status code (unauthorized access) but if you'd instead like to return an ActionResult such as a RedirectToRoute or a View, you could set a value to the filterContext.Result property.

I end up doing this task as below:
On successful completion of every page, save the page Name in database
When user request for a new page, simply check the page they have completed last from database and decide what to do.
I chooses this approach simply because it will definitely help during Problem Solving/Troubleshooting once application is in Production, which is huge for me.
Thanks everyone for your responses!

Related

How to return to intended page in this case

I have the typical scenario of an online newspaper where you have the article and below a form to comment, but where you need to login to comment. So on clicking it takes you to the log page and it should return you to that page once you have authenticated.
So, because the form is part of a page that the user can see without being logged in, I cannot write the middleware for that page created by a PostController, (get route).
The CommentController only has one method, which is the store one for the Form. so, of course, if I placed a middleware for that controller, it would fail because although it would indeed take you to the login page on clicking the submit button, the Intended URL saved would be that Post for the form, so on returning after authenticating, it would take you to a non existent URL page under the name of the Post route for that Form.
I have read about Auth::guards and the like but could not come clear with it,
This
Laravel 5 - After login redirect back to previous page
asks exactly the same question that I do, it is the same scenario, but I dont see how his answer works, because defining a protected variable (and I have that already) like protected $redirectTo in the Auth controller only tells where to go after authenticating, and it is only a fixed route, in my case it takes you to the dashboard. But I dont want to be taken to the dashboard, it has to return to the page where the article and the comment form are.
I found the solution at Laracasts. I must say I really dont understand it, but it works. It adds two functions to the AuthController.
https://laracasts.com/discuss/channels/laravel/redirect-to-page-where-login-button-was-clicked
public function showLoginForm()
{
if(!session()->has('from')){
session()->put('from', url()->previous());
}
return view('auth.login');
}
public function authenticated($request,$user)
{
return redirect(session()->pull('from',$this->redirectTo));
}
It's been a while since i've done this, but this should work normally.
You could add a GET param in your link from the comment section to login page.
http://....com/login?intended=http://yourredirectlink.com/#form
Put the intended url in in the session variable url.intended and after login you redirect like so
Redirect::intended('/');
This will redirect back to the the url '/' if the session variable is not available.

ASP.Net MVC 3 Querystring Parameter

I am developing an ASP.Net MVC 3 Web Application. Within some of my Views I display tabular data to the user, and beside each record in the table there is an Edit link. When the user clicks this link it takes them to an edit page where they can edit and update the record.
My issue is that once the user clicks the edit link, the URL becomes something like this
http://www.mytestsite.com/myData/edit/3
The '3' is the ID of the record to be updated, however, there is nothing stopping the user from changing the '3' to another digit, and this then means they can edit potentially a record which does not belong to them.
Does anyone have a solution on how I can prevent this from happening?
Thanks for you help.
You need to introduce Authentication and Authorisation into your application. Here is one article of many out there on how to get started with this. You will additionally need to work out how to store logged on user identity and then how to attach this to the record when it was created in the first place. You must then validate, on the server, that the subsequent edit request is being made by the user who created the record in the first place (or by a user who has a role on your system which allows them to do this, such as an Administrator).
Even if the ID wasn't being displayed on the URL a malicious user could still manipulate the HTTP Request to pass an ID of their choice. In any secure system you should always, always, always validate that the currently logged on user genuinely has permission to carry out the requested action. You should never rely on what comes back from the browser to determine this (aside from the authentication context which is managed securely by the MVC framework. Usually).
I believe you should have the information about who have the edit permission on this purticular resource, in your tables. Ex : in your table you might have the "CreatedById" column where you store the ID of the user who created this record. Now in your edit action method, you check the "CreatedById" of the current Item is same as of the "UserId" of the Current user (you maye get this from the session, if you stored it there). Something like this.
public ActionResult Edit(int id)
{
int currentUserID=1; // TO DO : get this value from session or somewhere
ProductVieWModel product=myRepo.GetProduct(id);
if(product!=null)
{
if(product.CreatedById==currentUserID)
{
return View(product);
}
else
{
return View("NotAutherized");
}
}
return View("ProdcutNotFound");
}
You should try using the GUID data type as it helps in these kind of situations, and the user cannot easily guess the next value

MVC3 application and keeping track of what page the user initially entered

and thanks for taking the time to read my question. We will have visitors to the site that might arrive at another user's profile page. We will then encourage them to login to the site, or register as new if they are not currently members. These actions move them pretty far away from that initial page/URL they stated on, but we want to be able to "jump them" back to that page after completing login/sign-up by "keeping track" where they began. (NOTE: we're using the standard Microsoft membership provider classes, if it matters.) The "jumping back" part seems straightforward, it is the "keeping track" part that is the problem.
An illustration/example:
A visitor arrives at the site after clicking: www.mysite.com/profiles/ID=108
The visitor then leaves that page to login. How can we best capture the ID=108 somehow, so that there is a good option for the (logged-in) user to return to that profile page? I understand from reading that cookies are not optimal, but some of the other options seem to throw a monkey wrench into my thinking since the user is not yet logged-in, etc.
Any concrete steps that I can take to address this in the best MVC3 way?
EDIT:
public ViewResult MyProfile(int? id)
{
HttpCookie cookie = new HttpCookie("MySiteCookie");
cookie.Value = id.ToString();
cookie.Expires = DateTime.Now.AddYears(1); //or whatever time is appropriate
System.Web.HttpContext.Current.Response.Cookies.Add(cookie);
//do some other stuff
}
At the other end, to make use of the cookie, I've put it into the menu (temporarily) in such a way:
var MyProfileId = "";
if (Request.Cookies["MySiteCookie"] != null)
{HttpCookie cookie = request.Cookies["MySiteCookie"];
MyProfileId = Server.HtmlEncode(cookie.Value);}
and then you put it into your link
You have a couple of options:
1) You can use Session in your Controller to store the value:
Session.Remove("ID");
Session.Add("ID", "108")
And retrieve in the called Controller.
ID = Session["ID"];
2) You can pass the ID=108 on the query string from the Login Controller:
return RedirectToAction("Edit", "Profile", new { ID = "108" });
I understand from reading that cookies are not optimal
IMHO cookies are the best way to approach this. When an anonymous user lands on the profiles page simply emit a cookie containing the id of the profile that he is currently viewing. Then later when he successfully logs in read this cookie value to obtain the id and construct the redirect link and redirect him to this link.
Another possibility is to use server side session but I am mentioning this just as an alternative to cookies and not at all as something that I would recommend.
You can make the redirection to the login action adding a Url to a query string param.
Let say: www.mysite.com/login?ReturnUrl='www.mysite.com/profiles/ID=108'
I think that this is the default membership provider behaviour, but you can get the ReferrerUrl to place it in the query string on your own.

Salesforce Session variables, set and get variables in Session

I want to be able to read / write some variables to the current session in my Salesforce site pages.
I have a site built using Salesforce Sites, I need to store/retrieve some values across all the pages (consider that I am building something similar to a shopping cart).
However I cant find any good example on how to read and write variables to the session (anonymous user).
I am using Visualforce pages with several controllers built in Apex.
Regards
If you are building something like a shopping cart, or a "wizard" where you need to keep controller variables in context from one page view to another, then the best way to do this in VisualForce is to use the same controller.
When the user submits a form ( through actionFunctions, commandButtons, or commandLinks, etc.), and your controller returns a page Reference, the view state is preserved if the new visual force page uses the same controller.
In this way, you could, for example, have the user enter their name and email address using apex:inputField tags on page one. They navigate to page two, which uses the same controller as page one, and the page could reference the same controller variables. Essentially, the controller is still in scope, and so are all the variables that were updates.
Example:
Page one:
<apex:page controller="myController">
Please enter your name <apex:inputText value="{!shopper_name}"/>
<br/>
<apex:commandButton action="{!pageTwo}" value="Click for page two"/>
</apex:page>
Page two:
<apex:page controller="myController">
You entered: <apex:outputText value="{!shopper_name}" />.
</apex:page>
Controller:
public class myController {
public string shopper_name { get; set; }
public myController() {
shopper_name = null;
}
}
Custom settings are cached at the application level, maybe that's why it was suggested in the link above. I'm not sure if I'd recommend that approach, but you might be able to get it to work.
If you create a Custom Setting named "SessionData", and add your custom fields (that represent the data you want to store in session), you could save data to it like this:
Database.SaveResult result = Database.insert(new SessionData__c(YourFieldHere='Your value here etc'));
System.debug(result.getID());
Then use the resulting custom setting ID to store in a cookie. While custom settings can be accessed using normal SOQL, the advantage is that the data is cached and can be accessed like this:
if (SessionData__c.getAll().containsKey('unique ID from cookie here'))
{
System.debug(SessionData__c.getInstance('unique ID from cookie here').YourFieldHere);
}
Keep in mind that custom settings weren't really designed for this, so you'll need to periodically purge old custom settings data, as normal session management systems do.
See the Apex Custom Settings documentation for more details.
I think Visualforce View State might be useful to you:
Visualforce pages that contain a form component also contain an encrypted, hidden form field that encapsulates the view state of the page. This view state is automatically created, and as its name suggests, it holds the state of the page - state that includes the components, field values and controller state.
You should use Javascript cookies for this.
You could also use Apex cookies, but then you'd need to make sure that each request hits the server (and not the caching layer).
for Apex Cookie you can use following code:
//Setting Cookie
public void setCookie() {
Cookie userCookie = new Cookie('CookieName', fieldValueToBeStoredAsCookie, null, 315569260, false); //Here 315569260 represents cookie expiry date = 10 years. You can set this to what ever expiry date you want. Read apex docs for more details.
ApexPages.currentPage().setCookies(new Cookie[] {
userCookie
});
}
//Reading Cookie
Cookie cookie = ApexPages.currentPage().getCookies().get('CookieName');
if (cookie != null) {
String fieldValueToBeStoredAsCookie = cookie.getValue();
}

Spring security - same page to deliver different content based on user role

i tried to search for any previous post related to my issue but couldnt find any. I have a scenario where in page handles 3 different scenarios and one of them not working. This page returns different content depending on if the user is authenticated or anonymous.
localhost:8080/myApp/muUrl?test=authenticatedContent - > used for Scenario 1 & 2
localhost:8080/myApp/muUrl?test=anonymousContent -> used for Scenario 3
Scenario:
1) Authenticated user accesing the page url - the user gets displayed correct information. Works fine
2) Anonymous user accesing page URL with parameters that requires authentication - If anonymous, there is second level of check on the content they are accessing. for example, based on the GET parameters, there is custom logic to determine if the user has to be authenticated. In which case the page gets redirected to login page (WORKS fine).
3) Anonymous user accessing page URL with parameters that doesnt need authentication - in this case i get the SAvedRequest and redirect to the URL which is taking me to an infinite loop.
Am i missing something very obvious or is there a way in AuthenticationProcessFilterEntryPoint to say "DON'T redirect to LOGIN page but process it" ?
thanks.
I found a solution at last (someone suggested it to me on the Spring forums).
The idea is to use the #PreAuthorize annotation in the controllers as described here: see here
See code sample below:
#RequestMapping("/")
#PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
return "authenticatedHomePage";
}
#RequestMapping("/")
public String homePage() {
return "homePage";
}

Resources