I've been playing with some of the MVC tutorials on making a database website with C# and have a question about how to make a section of the website only accessible ONCE a user has logged in with a username and password.
I have a logon page (code below), which takes the username and password, and then authenticates a user against a database record. Once you've clicked the "login" button the return URL takes you into an Admin section of the website (full path is: http://localhost:53559/Data/update). This bit I'm happy with. However, the issue I have is that the "update" page is still accessible if you've NOT logged in I.e. if I enter in the browser the path above (http://localhost:53559/Data/update) without ever logging in first it will load no problem).
How do I restrict the update page, so it is only available once the user has logged in?
(NB: total beginner, small words please!)
==================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using DFAccountancy.Models;
namespace DFAccountancy.Controllers
{
public class AdminController : Controller
{
//
// GET: /Admin/LogOn
public ActionResult LogOn()
{
return View();
}
//
// POST: /Account/LogOn
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Update", "Data");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Account/LogOff
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
==================================================================================
@model DFAccountancy.Models.Data
@{
ViewBag.Title = "Update";
}
<h2>Update</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () { $("#cl_button1").click(function () { $("#para1").val(""); }); });
$(function () { $("#cl_button2").click(function () { $("#para2").val(""); }); });
</script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Data</legend>
<div class="editor-label">
@Html.LabelFor(model => model.para1)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.para1, new { cols = 75, @rows = 5 })
@Html.ValidationMessageFor(model => model.para1)
<input id="cl_button1" type="button" value="Clear Paragraph" />
</div>
<div class="editor-label">
@Html.LabelFor(model => model.para2)
</div>
<div class="editor-field">
@Html.TextAreaFor(model => model.para2, new { cols = 75, @rows = 5 })
@Html.ValidationMessageFor(model => model.para2)
<input id="cl_button2" type="button" value="Clear Paragraph" />
</div>
<p>
<input type="submit" value="Update" />
<input type="reset" value="Re-Set to begining" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
==================================================================================
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DFAccountancy.Models;
namespace DFAccountancy.Controllers
{
public class DataController : Controller
{
private DataDBContext db = new DataDBContext();
//
// GET: /Data/
public ViewResult Index()
{
return View(db.Data.ToList());
}
//
// GET: /Data/Details/5
public ViewResult Details(string id)
{
Data data = db.Data.Find(id);
return View(data);
}
//
// GET: /Data/Update
public ActionResult Update()
{
var model = db.Data.FirstOrDefault();
return View(model);
}
//
// POST: /Data/Update
[HttpPost]
//[Authorize(Roles = "Administrator")] //Created Validataion so inaccessible from outside
[ValidateInput(false)]
public ActionResult Update(Data data)
{
if (ModelState.IsValid)
{
data.ID = 1; //EF need to know which row to update in the database.
db.Entry(data).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(data);
}
}
}
Use the [Authorize]
filter. You can apply it to a controller, or to an individual action.
[Authorize]
public class DataController : Controller
{...
Or
[Authorize]
public ActionResult Update()
{...
As a side note, you are not closing your DB connection from what I can see. Your data context needs to have .Dispose()
called on it when it is finished.
EDIT
Moreover, it looks like your get method is not decorated with the authorize, which is why anyone can navigate there. Only the post is decorated, or was until commented out, with the authorize filter. [HttpGet]
is used for a basic request, whereas the [HttpPost]
generally comes from a form post (Sometimes it is done through ajax).