I'm trying to figure out someone else code....
what this does is taking the username and password from the user and checks from the database table whether username and password is correct....
his DataLayer looks like this:
public class UserRepository : IUser
{
Context con = new Context();
public UserDTO GetUser(string user)
{
User u = con.Users.Find(user);
UserDTO User = new UserDTO();
if (u != null)
{
User.Username = u.UserName;
User.Password = u.Password;
}
return User;
}
}
IUser interface:
public interface IUser
{
UserDTO GetUser(string user);
}
ServiceLayer looks like this:
public class UserService : IUserService
{
IUser data;
public UserService(IUser data)
{
this.data = data;
}
public bool Authenticate(string user,string pwd)
{
UserDTO u = data.GetUser(user);
if (u.Username == user && u.Password == pwd)
return true;
else
return false;
}
}
IuserService Interface:
public interface IUserService
{
bool Authenticate(string user, string pwd);
}
And the MVC Controller Looks like this:
public class HomeController : Controller
{
public ActionResult Log()
{
return View();
}
IUserService ser;
public HomeController()
{
ser = new UserService(new UserRepository());
}
public ActionResult Login(Models.User user)
{
if (ser.Authenticate(user.UserName, user.Password))
{
ViewBag.Message = "Success";
}
else
ViewBag.Message = "UnSuccess";
return View();
}
I can't really understand what he has done in this Controller Constructor (ser = new UserService(new UserRepository())) but the code works perfectly ....
What is he trying to do, is he trying to Connect this controller into the DataLayer(UserRepository class)?
Thank you!!!
The home controller has a Login Action method which accepts a user model, which has a username and password.
public ActionResult Login(Models.User user)
The action method uses the user service to try to authenticate the user's password.
The controller calls the UserService's Authenticate method which takes in the supplied username and password.
if (ser.Authenticate(user.UserName, user.Password))
The service calls the UserRepositories GetUser method, which tries to find a user by the username.
public bool Authenticate(string user,string pwd)
{
UserDTO u = data.GetUser(user);
...
User u = con.Users.Find(user);
If it finds a user by that username in the db, it check returns the user object with the username and password.
if (u != null)
{
User.Username = u.UserName;
User.Password = u.Password;
}
return User;
The rest of the authenticate method checks if this UserDTO's password matches with the initial password supplied to the Login Action method, and returns true or false depending on that.
if (u.Username == user && u.Password == pwd)
return true;
else
return false;
Related
I have created a simple register/login system and right now when the user registers it just stores their plain password in the database. I am wondering how I can hash it then when they login, how do I unhash it and compare it to what they put in.
All I have in my users table/model class is username and password
UsersController
// GET: Register User
public ActionResult Register(User user)
{
if (ModelState.IsValid)
{
using (UserContext db = new UserContext())
{
db.Users.Add(user);
db.SaveChanges();
}
ModelState.Clear();
}
return View();
}
// POST: Login User
[HttpPost]
public ActionResult Login(User user)
{
using (UserContext db = new UserContext())
{
var usr = db.Users.SingleOrDefault(u => u.Username == user.Username && u.Password == user.Password);
if (usr != null)
{
Session["UserID"] = usr.Username.ToString();
return RedirectToAction("Index", "Profile");
}
else
{
ModelState.AddModelError("", "Username or Password Incorrect");
}
}
return View();
}
I have a MVC project with 2 areas: Admin and Client. I also have a login page in the main controller. What I want to do is to Authenticate a user based on its roles. If the user is for client they can't login to admin and the other way around.
For example if you try Localhost/admin, the code checks if the user is authorised. If not it redirects you to Localhost/admin/AccountLogin. The same for Localhost/client to Localhost/client/account/login.
I want to use a customAuthorize rather than [Authorize(Roles="Admin")].
everything works fine if I don't use roles, but the problem is if you login as client you can simply change the url and go to admin. So I tried to use roles.
In admin area:
An account Controller:
public class AccountController : MainProject.Controllers.AccountController
{ }
A home controller:
[CustomAuthorize("Admin")]
public class HomeController : Controller
{
public ActionResult HomePage()
{
return View();
}
}
The custom Authorise:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string _loginPage { get; set; }
private string _customRole { get; set; }
public CustomAuthorizeAttribute(string userProfilesRequired)
{
_customRole = userProfilesRequired;
_loginPage = "/" + _customRole + "/Account/Login";
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var formsIdentity = filterContext.HttpContext.User.Identity as System.Web.Security.FormsIdentity;
// I want to check if the role of current user is the same as the controller If not redirect to the /account/login page.
var validRole = this.Roles == _customRole;//filterContext.HttpContext.User.IsInRole(_customRole);
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (!validRole)
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
}
else
{
filterContext.HttpContext.Response.Redirect(_loginPage);
}
base.OnAuthorization(filterContext);
}
}
The Account Controller in Main Controller:
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
return View();
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
//I need to set the roles here but not sure how
return RedirectToAction("homePage", "Home", new { area = GetArea() });
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
}
and it the web config:
<forms loginUrl="~/Account/Login" timeout="200" />
</authentication>
<authorization>
<allow roles="Admin,Client" />
</authorization>
I searched a lot in the web but couldn't find a proper answer. I appreciate if you Could help me out to correctly implement this authorisation in MVC.
I just want to know how can I set a role to a user when login. At the moment if I set a user in login, it can't remember when it gets to CustomAuthorize class.
Any help?
Cheers,
There are a lot of ways to this but I will tell you what I used in this case.
You don't actually need to create a custom Authorization Attribute but instead make use of PostAuthenticateRequest event Handler in Global.asax given that you have a "table" roles in your database.
Add the code below in Global.asax
public override void Init()
{
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated && User.Identity.AuthenticationType == "Forms")
{
string[] roles = GetRoleOfUser(Context.User.Identity.Name);
var newUser = new GenericPrincipal(Context.User.Identity, roles);
Context.User = Thread.CurrentPrincipal = newUser;
}
}
public string[] GetRoleOfUser(string username)
{
string[] usersInRole;
// Get the Role of User from the Database
// Should be of String Array
// Example Query to Database: 'Select UserRole FROM User WHERE Username = "arash"'
// It doesnt matter if user has One or more Role.
return usersInRole;
}
Then your account controller should be this.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string ReturnUrl)
{
try
{
if (ModelState.IsValid)
{
if (model.UserName == "Arash" && model.Password == "123")
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
return RedirectToAction("HomePage", "Home");
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
catch (Exception ex)
{
ModelState.AddModelError("", "Error: " + ex.Message);
return View(model);
}
}
Now for example there is an Action in your HomeController that can only be access by Admin. You can just decorate the action with Authorize attribute like this below.
HomeController.cs
[Authorize(Roles = "Admin")]
public ActionResult AdminHomepage()
{
//For Admin Only
return View();
}
[Authorize(Roles = "Client")]
public ActionResult ClientHomepage()
{
//Client only Homepage, User with Role "Admin" cant go here.
return View();
}
[AllowAnonymous]
public ActionResult HomePageForAll()
{
//For Everyone
return View();
}
[Authorize(Roles = "Client,Admin")]
public ActionResult HomePageForClientAndAdmin()
{
return View();
}
public ActionResult HomePage()
{
return View();
}
The user will be redirected to Login URL if they are not authorized given that it is specified in Web.config (Which you already have set).
I have an action method and that can be accessed by Admin only
// Action Methods
[AuthorizationService] // My custom filter ,you can apply at controller level
public ActionResult ProjectList(Employee emp)
{
// do some work
}
//Employee class
public class Employee
{
string Name{get;set;}
string Role{get;set;}
}
// My custom filter
class AuthorizationService : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Employee = filterContext.ActionParameters["emp"] as Employee;
if (Employee.Role!="Admin")
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new { action = "Login", Controller ="Home"}));
}
}
}
I want to implement my custom authorization, I wonder what is wrong with my code even I got the user credentials correctly it still redirects me to my Login Method, please see the code below
Edit: I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
Forms Auth Ticket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
I have successfully implemented the Authorize Attribute with Roles, for future readers please see code below.
Login Controller
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login (AdminViewModels.Login viewModel, string returnURL)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
PasswordHasher passwordVerify = new PasswordHasher();
var query = (from acc in db.accounts.Where(x => x.username == viewModel.Username)
select new { acc.username, acc.password}).FirstOrDefault();
if (query != null)
{
if (ModelState.IsValid)
{
var result = passwordVerify.VerifyHashedPassword(query.password, viewModel.Password);
switch (result)
{
case PasswordVerificationResult.Success:
//set forms ticket to be use in global.asax
SetupFormsAuthTicket(viewModel.Username, viewModel.rememeberMe);
return RedirectToLocal(returnURL);
case PasswordVerificationResult.Failed:
ModelState.AddModelError("", "Wrong Username or Password");
return View(viewModel);
}
}
}
return View(viewModel);
}
FormsAuthTicket
private account SetupFormsAuthTicket(string userName, bool persistanceFlag)
{
account user = new account();
var userId = user.id;
var userData = userId.ToString(CultureInfo.InvariantCulture);
var authTicket = new FormsAuthenticationTicket(1, //version
userName, // user name
DateTime.Now, //creation
DateTime.Now.AddMinutes(20), //Expiration
persistanceFlag, //Persistent
userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
return user;
}
Global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//take out user name from cookies
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string[] roles = null;
trainingEntities db = new trainingEntities();
//query database to get user roles
var query = (from acc in db.account_roles where acc.account.username == username select acc.role.role_name).ToArray();
roles = query;
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles);
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Now you can use [Authorize(Roles = "Admin")]
to any action method or on top of controller
as I see in ControllerLogin attribute it is now being applied in a variable, when it should be applied to a method or a class
[CustomAuthorization(UserRole="Admin")]
// GET: Manage
private trainingEntities db = new trainingEntities();
public ActionResult Index()
{
return View();
}
Private trainingEntities dB = new TrainingEntities();
[CustomAuthorization(UserRole="Admin")]
Public ActionResult Index()
{
//yourcode
}
I have this custom authorization class to check if a user is an admin:
public class IsAdminAttribute : AuthorizeAttribute
{
private datacontext() db = new datacontext();
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var currentUser = httpContext.User.Identity.Name;
return db.Users.Where(u => u.UserName == currentUser).Where(ut => ut.UserTypeID == 2).Count() == 1 ? true : false;
}
return isAuthorized;
}
}
and is used here:
[IsAdmin]
public ActionResult CreateUser()
{
ViewBag.UserTypeID = new SelectList(db.UserTypes, "UserTypeId", "Name");
return View();
}
and works ok but takes me back to my log in page when the user is not authorized. What I want to happen is for the user to be redirected somewhere with an error message popping up. How do I handle the denied access event?
How do I handle the denied access event?
Simply override the HandleUnauthorizedRequest method and return directly the view you like:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "Unauthorized"
};
}
This will render ~/Views/Shared/Unauthorized.cshtml. You could also pass view models, master pages, etc... to this ViewResult.
I wish to lock out access to a user's EDIT page (eg. /user/pure.krome/edit) if
a) Identity.IsAuthenticated = false
or they are authenticated but
b) Idenitity.Name != user name of the user page they are trying to edit
c) Identity.UserType() != UserType.Administrator // This is like a Role, without using RoleProviders.
I'm assuming u can decorate a controller or a controller's action method with something(s), but i'm just not sure what?
Look at the AuthorizeAttribute.
ASP.Net MVC: Can the AuthorizeAttribute be overriden?
A custom attribute derived from AuthorizeAttribute is what I use to do this. Override the OnAuthorize method and implement your own logic.
public class OnlyUserAuthorizedAttribute : AuthorizeAttribute
{
public override void OnAuthorize( AuthorizationContext filterContext )
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizeResult();
}
...
}
}
I implemented the following ActionFilterAttribute and it works to handle both authentication and roles. I am storing roles in my own DB tables like this:
User
UserRole (contains UserID and RoleID foreign keys)
Role
public class CheckRoleAttribute : ActionFilterAttribute
{
public string[] AllowedRoles { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string userName = filterContext.HttpContext.User.Identity.Name;
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (AllowedRoles.Count() > 0)
{
IUserRepository userRepository = new UserRepository();
User user = userRepository.GetUser(userName);
bool userAuthorized = false;
foreach (Role userRole in user.Roles)
{
userAuthorized = false;
foreach (string allowedRole in AllowedRoles)
{
if (userRole.Name == allowedRole)
{
userAuthorized = true;
break;
}
}
}
if (userAuthorized == false)
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
}
}
else
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + String.Format("?ReturnUrl={0}", filterContext.HttpContext.Request.Url.AbsolutePath), true);
}
}
I call this like this...
[CheckRole(AllowedRoles = new string[] { "admin" })]
public ActionResult Delete(int id)
{
//delete logic here
}