I'm creating ASP.NET MVC 4 Internet Application.
In that Application I created Login Page that any user can log in to, then I allowed to redirect user to different pages based on their role.
ASP.NET Identity is the membership system here.
This is my Login Controller method:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
if (user.ConfirmedEmail == true)
{
await SignInAsync(user, model.RememberMe);
if (String.IsNullOrEmpty(returnUrl))
{
if (UserManager.IsInRole(user.Id, "HEC_Admin"))
{
return RedirectToAction("Index", "HEC");
}
//role Admin go to Admin page
if (UserManager.IsInRole(user.Id, "HEI_User"))
{
return RedirectToAction("Index", "HEI");
}
}
else
{
return RedirectToLocal(returnUrl);
}
}
else
{
ModelState.AddModelError("", "Confirm Email Address.");
}
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is HEI Controller Class:
public class HEIController : Controller
{
//
// GET: /HEI/
[Authorize(Roles = "HEI_User")]
public ActionResult Index()
{
return View();
}
}
This is my HEC Controller Class:
public class HECController : Controller
{
//
// GET: /HEC/
[Authorize(Roles = "HEC_Admin")]
public ActionResult Index()
{
return View();
}
}
when I remove [Authorize(Roles = "HEC_Admin")] above the index action in HECController class and when I remove [Authorize(Roles = "HEC_User")] above the index action in HEIController class this is working fine,
but then How restrict unauthorized access to these pages?
I had the same problem as you and I still don't know the reason why it happens. What I did was to create my own custom Authorization Attribute and check the Roles myself.
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public string IdentityRoles
{
get { return _identityRoles ?? String.Empty; }
set
{
_identityRoles = value;
_identityRolesSplit = SplitString(value);
}
}
private string _identityRoles;
private string[] _identityRolesSplit = new string[0];
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//do the base class AuthorizeCore first
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
if (_identityRolesSplit.Length > 0)
{
//get the UserManager
using(var um = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
var id = HttpContext.Current.User.Identity.GetUserId();
//get the Roles for this user
var roles = um.GetRoles(id);
//if the at least one of the Roles of the User is in the IdentityRoles list return true
if (_identityRolesSplit.Any(roles.Contains))
{
return true;
}
}
return false;
}
else
{
return true;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//if the user is not logged in use the deafult HandleUnauthorizedRequest and redirect to the login page
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
//if the user is logged in but is trying to access a page he/she doesn't have the right for show the access denied page
{
filterContext.Result = new RedirectResult("/AccessDenied");
}
}
protected static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
I also added the HandleUnauthorizedRequest method to redirect to a appropriated page if the user has is logged in but has no access to this action or controller
To use it just do this:
[CustomAuthorization(IdentityRoles = "HEI_User")]
public ActionResult Index()
{
return View();
}
Hope it helps.
Related
How can I implement a function that only the user named Admin can access?
Its important to do this without roles!
Like:
[Autohrize] => logged in users
But i want:
if username is like admin => you can access the page
Thank you
If i get you correct, you want to authorize user with user name. You can do that with custom authorize attribute.
public class UserNameAuthorizationAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
base.AuthorizeCore(httpContext);
dynamic user = HttpContext.Current.Session["user"];
return user.name == "Admin";
}
}
More detail can be found here
The different options are listed in this MSDN article. The option you want is the decorator "[Authorize(Users = "admin")]" for the SpecificUsersOnly method.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[Authorize]
public ActionResult AuthenticatedUsers()
{
return View();
}
[Authorize(Roles = "Admin, Super User")]
public ActionResult AdministratorsOnly()
{
return View();
}
[Authorize(Users = "admin")]
public ActionResult SpecificUserOnly()
{
return View();
}
}
My login methot,
A cookie incoming data is put,
public async Task<ActionResult> Login(string username, string password)
{
try
{
RepairService.EmployeeServiceClient srv = new RepairService.EmployeeServiceClient();
var CurrentEmployee = await Task.Run(() => srv.LoginEmployee(username, password));
if (CurrentEmployee != null)
{
var model = new EmployeeDetail
{
EmloyeeId = CurrentEmployee.EmployeeId,
//...//
};
HttpCookie cookie = new HttpCookie("userCookie");
cookie.Value = JsonConvert.SerializeObject(model);
cookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(cookie);
return Json(model);
}
}
catch (Exception)
{
return Json(null);
}
return Json(false);
}
So you can control when users login cookie is not empty.
public ActionResult IsLoggedIn()
{
return Json(Request.Cookies["userCookie"] != null && Request.Cookies["userCookie"].Value != "");
}
When I call to get the value of this method Request is null. Where can I make mistakes ?
public ActionResult GetCurrentUser()
{
if (Request.Cookies["userCookie"] != null && Request.Cookies["userCookie"].Value == "Test")
{
var employee = (EmployeeDetail)JsonConvert.DeserializeObject(Request.Cookies["userCookie"].Value);
return Json(employee);
}
return Json(false);
}
In my opinion you're doing your Auth verification in the wrong way. You should not create an Action in your Controller responsible for that. You must create an ActionFilter, and decorate your Actions or your Controller's, or even register this filter globally to verify this for every request. For example:
ActionFilter
public class AuthorizedFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Do your logic here
var cookieValue = filterContext.HttpContext.Request.Cookies["userCookie"]).FirstOrDefault();
if(cookieValue != null)
return;
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Home"},
{"action", "Login"},
});
}
}
Controller
You could use at Controller Level, wich means, it will apply to all Actions
[AuthorizedFilter]
public class SomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Or you could use at Action level
public class SomeController : Controller
{
[AuthorizedFilter]
public ActionResult Index()
{
return View();
}
}
Here's my Sign Up and Login action methods in the controller:
public ActionResult SignUp()
{
return View();
}
[HttpPost]
public ActionResult SignUp(User _user)
{
_user.Authorize = CustomRoles.RegisteredUser;
int lastuserid = entities.Users.Last().UserID;
_user.UserID = lastuserid + 1;
if (ModelState.IsValid)
{
Roles.AddUserToRole(_user.UserName, CustomRoles.RegisteredUser);
entities.Users.Add(_user);
entities.SaveChanges();
RedirectToAction("Index");
}
return View(_user);
}
public ActionResult Login()
{
LoginViewModel LVM = new LoginViewModel();
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
LVM.UserName = existingCookie.Value;
}
return View(LVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel u)
{
if (ModelState.IsValid)
{
if (u.RememberMe==true)
{
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
existingCookie.Value = u.UserName;
existingCookie.Expires = DateTime.Now.AddHours(-20);
}
HttpCookie newCookie = new HttpCookie("UserName", u.UserName);
newCookie.Expires = DateTime.Today.AddMonths(12);
Response.Cookies.Add(newCookie);
}
var v = entities.Users.Where(a => a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null)
{
System.Web.HttpContext.Current.Session["UserName"] = u.UserName;
return RedirectToAction("Index");
}
}
return View(u);
}
and here's a sample of the action methods they're supposed to go to, some of them are in a different controller but the outcome is the same for all of them:
[Authorize(Roles = CustomRoles.RegisteredUser)]
public ActionResult Orders(User U)
{
return View();
}
[Authorize(Roles = CustomRoles.Manager)]
public ActionResult Stock()
{
return View(entities.Cars.ToList());
}
what happens is i get redirected back to the Login method, which is what should be happening if a user isn't logged in, but a user is logged in and it's still hapening
You are trying to implement Form Authorization, but as i think you forgot about authorize attribute use HttpContext.User.IsInRole method to detect is user can access to action. To resolve your problem you can configure forms auth via your web.config or you manually assign your HttpContext.User via HttpModule or with application events in Global.asax.cs like this for example:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null) {
context = new new GenericPrincipal(new GenericIdentity(existingCookie.Value), new string[]{"Admin", "Manager"});
}
}
I am using MVC4 and I'm trying to modify the allocation procedure for authenticating a user and assign roles to users. Everything works fine for the attribute [Authorize (Users = "adminadmin")], but the [Authorize (Roles = "Admin")] every time there is a login page and lack of access.
Global.asax.cs:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// look if any security information exists for this request
if (HttpContext.Current.User != null)
{
// see if this user is authenticated, any authenticated cookie (ticket) exists for this user
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// see if the authentication is done using FormsAuthentication
if (HttpContext.Current.User.Identity is FormsIdentity)
{
// Get the roles stored for this request from the ticket
// get the identity of the user
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
//Get the form authentication ticket of the user
FormsAuthenticationTicket ticket = identity.Ticket;
//Get the roles stored as UserData into ticket
List<string> roles = new List<string>();
if (identity.Name == "adminadmin")
roles.Add("Admin");
//Create general prrincipal and assign it to current request
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles.ToArray());
}
}
}
}
AccountController:
[InitializeSimpleMembership]
public class AccountController : Controller
{
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
// Lets first check if the Model is valid or not
if (ModelState.IsValid)
{
string username = model.UserName;
string password = model.Password;
bool userValid = username == password ? true : false;
// User is valid
if (userValid)
{
FormsAuthentication.SetAuthCookie(username, false);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
}
HomeController.cs:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
[Authorize]
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
[Authorize(Roles = "Admin")]
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Web.config:
(...)
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880"/>
</authentication>
(...)
You're almost there. Right now what's happening is that you set the principal to a custom principal, and the SimpleMembership provider comes in after you and blows away your principal by setting it to a System.Web.Security.RolePrincipal. Move your current Application_AuthenticateRequest code into a new Application_PostAuthenticateRequest handler and your custom principal will remain in place.
This is what you want (although it uses a custom membership)
http://mycodepad.wordpress.com/2014/05/17/mvc-custom-authorizeattribute-for-custom-authentication/
I've recently created an ASP.NET MVC 2 application, which works perfectly in the development environment. However, when I deploy it to the server (123-reg Premium Hosting), I can access all of the expected areas - except the Account controller (www.host.info/Account). This then attempts to redirect to the Error.aspx page (www.host.info/Shared/Error.aspx) which it cannot find. I've checked that all of the views have been published, and they're all in the correct place.
It seems bizarre that two other controllers can be accessed with no problems, whereas the Account controller cannot be found. I have since renamed the AccountController to SecureController, and all of the dependencies, to no avail.
The problem with not being able to find the Error.aspx page also occurs on the development environment.
Any ideas would be greatly appreciated.
Thanks,
Chris
1) Can you check the DLL that was published to make sure that type exists in the assembly? Are any special modifiers applied to the Account controller compared to the other controllers (such as specific attributes, base classes, additional code)?
2) Can you verify what HttpMethod you are using to request the page? I'm assuming just a normal GET, but it may be coming in as a different verb causing you not to find your action method.
3) Are you using any custom routing, or just the standard {controller}/{action}/{id} setup?
The version of IIS on the server is 7.0, of which I have no control over.
The account controller code all works perfectly on the development environment, and the code is as follows:
[HandleError]
public class SecureController : Controller
{
private UserManager manager;
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (FormsService == null) { FormsService = new FormsAuthenticationService(); }
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
// Lazy man's Dependency Injection for now, use Ninject later!
public SecureController(UserManager mgr) { manager = mgr; }
public SecureController() : this(new UserManager(new PortfolioDataDataContext())) { }
// **************************************
// URL: /Account/LogOn
// **************************************
public ActionResult LogOn()
{
return View();
}
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
// **************************************
// URL: /Account/LogOff
// **************************************
public ActionResult LogOff()
{
FormsService.SignOut();
return RedirectToAction("Index", "Home");
}
// **************************************
// URL: /Account/Register
// **************************************
public ActionResult Register()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = manager.CreateUser(model.UserName, model.Password, model.Email, model.FullName);
if (createStatus == MembershipCreateStatus.Success)
{
FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View(model);
}
// **************************************
// URL: /Account/ChangePassword
// **************************************
[Authorize]
public ActionResult ChangePassword()
{
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View();
}
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
if (MembershipService.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword))
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
// If we got this far, something failed, redisplay form
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
return View(model);
}
// **************************************
// URL: /Account/ChangePasswordSuccess
// **************************************
public ActionResult ChangePasswordSuccess()
{
return View();
}
}