I'm building an intranet app using ASP.NET MVC 5.
My goal is to have the authentication of any user made by the Active Directory (i.e. I'm using the "Windows Authentication"), then add groups to any user inside the application (NOT using domain groups).
I've found some very interesting piece of code here:
http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/
But it's not working in my scenario: when I decorate the controller with [Authorize(Role="AppRole")], I can't be authorized even if the user (using Claims) is associated with the "AppRole" role.
This is my code:
In Global.asax.cs
void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
string[] roles = Utils.GetRolesForUser(User.Identity.Name);
var id = ClaimsPrincipal.Current.Identities.First();
foreach (var role in roles)
{
//id.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
id.AddClaim(new Claim(ClaimTypes.Role, #"Kairos.mil\Compliance"));
}
bool pippo = User.IsInRole("Compliance");
HttpContext.Current.User = (IPrincipal)id ;
bool pippo2 = User.IsInRole("Compliance");
}
}
The function GetRolesForUser is as follows (and is working fine):
public static string[] GetRolesForUser(string username)
{
dbOrdiniPersonaliEntities db = new dbOrdiniPersonaliEntities();
string utente = StripDomain(username);
string[] gruppi = new string[db.vGruppiUtentis.Where(t => t.KairosLogin == utente).Count()];
int i=0;
foreach (var gruppo in db.vGruppiUtentis.Where(t => t.KairosLogin == utente))
{
gruppi[i]=gruppo.GruppoDes;
i=i++;
}
return gruppi;
}
And the controller is decorated with the "standard" Authorize clause:
[Authorize(Roles="AppRole")]
public ActionResult Index(string sortOrder, string currentFilter, string DesSearchString,int? page)
{
// my code here
}
Any idea?
Thanks in advance
UPDATE
Thanks #Leandro
I've tried as you suggested the following code
void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
string[] roles = Utils.GetRolesForUser(User.Identity.Name);
ClaimsIdentity id = ClaimsPrincipal.Current.Identities.First();
foreach (var role in roles)
{
//id.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
id.AddClaim(new Claim(ClaimTypes.Role, #"Kairos.mil\Compliance"));
}
bool pippo = User.IsInRole("Compliance");
SetPrincipal((IPrincipal)id);
bool pippo2 = User.IsInRole("Compliance");
}
}
But I receive a run-time error when the code reaches this point
SetPrincipal((IPrincipal)id);
The error is as follows
Unable to cast object of type 'System.Security.Principal.WindowsIdentity' to type 'System.Security.Principal.IPrincipal'.
Thanks for your help
UPDATE 2 (maybe solved)
Hi
Looking deeper into SO, I've found this resource
ASP.NET MVC and Windows Authentication with custom roles
Following the answer of #Xhalent, I modified my code as follows
protected void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
String[] roles = Utils.GetRolesForUser(User.Identity.Name);
GenericPrincipal principal = new GenericPrincipal(User.Identity, roles);
Thread.CurrentPrincipal = HttpContext.Current.User = principal;
}
}
It seems now working fine! Any comments? Any drawbacks? Thanks a lot!!
Use this method to save the principal, so it sets up also in the thread:
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
Update: Also allow anonymous and test if User.IsInRole is getting something inside the method.
Related
I am new to ASP.NET MVC and learning how to custom Roles using FormAuthentication from this tutorial link
This code below is stored the Roles. It works fine when I perform this [Authorize(Roles="admin")] in the controller
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//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.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Is there a way to get the actual Role Name based on the current User.Identity? like below pseudo-code.
[Authorize]
public ActionResult Index()
{
bool isAdmin = System.Web.HttpContext.Current.User.IsInRole("admin"); // This also works correctly.
Response.Write("role: " + isAdmin);
string roleName = // The Code of How to get the actual Role Name
Response.Write("roleName: " + roleName); //e.g Admin, User...
return View();
}
From Comment: Do you know any good article about OWIN cookie
authentication for custom table for username and roles?
It has few pieces, so I created a sample project in GitHub AspNetMvcActiveDirectoryOwin. The original souce is to authenticate with AD, but you just need to modify ActiveDirectoryService class where you query custom tables.
The following three are the main classes -
AccountController
ActiveDirectoryService
OwinAuthenticationService replaces FormsAuthentication.
I've been trying to setup a new IdentityServer3 with AspNetIdentity for a few days now. I'm able to login using my existing Identity DB and that's all good but I can never get the User.Identity.Name to contain data.
I've tried multiple attempts at adding custom claims & scopes and adding scopes to clients.
Finally, I loaded up the IdentityServer3 Sample repository and tested it out with the webforms client project since it already used the User.Identity.Name in it's About page.
Using WebForms sample client + AspNetIdentity sample server = User.Identity.Name is always null
Using WebForms sample client + SelfHost with Seq sample server = User.Identity.Name with data
I've tried other sample host projects that all populate the User.Identity.Name value just fine.
Now, on the client side I've written a workaround to pull the 'preferred_username' claim value and set the 'name' claim with it.
var id = new claimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(userInfoResponse.GetClaimsIdentity().Claims);
//set the User.Identity.Name value
var name = id.Claims.Where(x => x.Type == "name").Select(x => x.Value).FirstOrDefault() ??
id.Claims.Where(x => x.Type == "preferred_username").Select(x => x.Value).FirstOrDefault();
id.AddClaim(new Claim("name", name));
My questions are:
Why doesn't the AspNetIdentity package fill this by default?
And what do I need to change on the server side so that I don't need to change the client?
public static IEnumerable<ApiResource> GetApis()
{
return new ApiResource[]
{
new ApiResource("MyApi", "My Admin API")
{
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }
}
};
}
In Identityserver4 you can add the UserClaims to your resource. Fixed it for me.
On IdentityServer4 you can implement IProfileService on server and add the Claim in GetProfileDataAsync
public class AspNetIdentityProfileService : IProfileService
{
protected UserManager<ApplicationUser> _userManager;
public AspNetIdentityProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//Processing
var user = _userManager.GetUserAsync(context.Subject).Result;
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
};
context.IssuedClaims.AddRange(claims);
//Return
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
//Processing
var user = _userManager.GetUserAsync(context.Subject).Result;
context.IsActive = (user != null) && ((!user.LockoutEnd.HasValue) || (user.LockoutEnd.Value <= DateTime.Now));
//Return
return Task.FromResult(0);
}
}
Then add "AddProfileService()" to your ConfigureServices method.
services.AddIdentityServer(...)
...
.AddProfileService<AspNetIdentityProfileService>();
i am doing this in order to authorize user.
[Authorize(Users = #"user1, user2, user3")]
public class MyController : Controller
{
// my stuff
}
i want to do authorization from the list of user which are in database table..
This is how I got it done:
Create a new class (which inherits from AuthorizeAttribute class).
public class CustomAuthorizeAttribute : AuthorizeAttribute
Override the AuthorizeCore method (in CustomAuthorizeAttribute class) and include your custom logic in it.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isUserAuthorized = false;
// custom logic goes here
// You can get the details of the user making the call using httpContext
// (httpContext.User.Identity.Name)
// Then get the information you have stored on your db, and compare it
// with these details.
// Set isUserAuthorized to true if the values match
return isUserAuthorized;
}
Decorate your controller action method with the attribute that you just created.
[CustomAuthorize]
public ActionResult DoSomething(string something, string someOtherThing)
This link form Gotalove is helpful.
try the following:
"using the link shared by #VikasRana http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
I got rid of my enum Role and my method
public CustomAuthorizeAttribute(params object[] roles)
{ ...}
I then changed Role in my model to be a string e.g. User.Role="Admin" instead of int. In my onAuthorization method I changed it to:
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Controller.TempData["ErrorDetails"] = "You must be logged in to access this page";
filterContext.Result = new RedirectResult("~/User/Login");
return;
}
if (filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Controller.TempData["ErrorDetails"] = "You don't have access rights to this page";
filterContext.Result = new RedirectResult("~/User/Login");
return;
}
}
and in my global.asax added this.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true && Request.IsAuthenticated== true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (GManagerDBEntities db = new GManagerDBEntities())
{
User user = db.Users.SingleOrDefault(u => u.Username == username);
roles = user.Role;
}
//let us extract the roles from our own custom cookie
//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.Split(';'));
}
catch (Exception)
{
//something went wrong
}
}
}
}
"
Source: Custom user authorization based with roles in asp.net mvc
PS.: In this link, in the same post, there is a second way to fix your problem.
In the bottom of the post.
If this can't to help you, you should try it to.
I am developing a project using ASP.NET MVC 3, now use a MembershipProvider, RoleProvider AuthorizeAttribute and custom. So in certain parts of the code use this:
[Logon(Roles = "login, test1")]
This code works perfectly, for use in the MembershipProvider code:
public override string [] GetRolesForUser (string username)
{
var = UsuarioRepository.GetListaPermissoesByUsuarioEmail permissions (username);
if (permissions == null)
{
nullPermissao var = new string [0];
nullPermissao return;
}
return permissions;
}
My question is. how can I use the following code, which method will need to customize?
I want to check is determined whether a particular type of user who is logged in and if it has certain privileges.
[Logon(Roles = "login, test1," Users = "User1")]
Using override string [] GetRolesForUser (string username) method it checks the Roles, in wich method I can check the User?
This should work out of the box with the AuthorizeAttribute. It checks if HttpContext.User.Identity.Name matches any of the terms you defined under AuthorizeAttribute.Users
As i see from the comments, you rolled your own LogonAttribute where your probably overwrote the OnAuthorize method. This is where the AuthorizeAtrribute does it`s magic.
Original ASP.NET MVC Source
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
IPrincipal user = httpContext.User;
return user.Identity.IsAuthenticated && (this._usersSplit.Length <= 0 || Enumerable.Contains<string>((IEnumerable<string>) this._usersSplit, user.Identity.Name, (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase)) && (this._rolesSplit.Length <= 0 || Enumerable.Any<string>((IEnumerable<string>) this._rolesSplit, new Func<string, bool>(user.IsInRole)));
}
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (OutputCacheAttribute.IsChildActionCacheActive((ControllerContext) filterContext))
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
if (this.AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), (object) null);
}
else
this.HandleUnauthorizedRequest(filterContext);
}
Did you mean to use the following?
[Authorize(Roles = "login, test1", Users = "User1")]
I am new MVC 3 user and I am trying to make admin through SQL database.
First of all, I have Customer entity and admin can be defined through admin field which is boolean type in Customer entity.
I want to make to access admin only in Product page, not normal customer.
And I want to make [Authorize(Roles="admin")] instead of [Authorize].
However, I don't know how can I make admin role in my code really.
Then in my HomeController, I written this code.
public class HomeController : Controller
{
[HttpPost]
public ActionResult Index(Customer model)
{
if (ModelState.IsValid)
{
//define user whether admin or customer
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["rentalDB"].ToString());
String find_admin_query = "SELECT admin FROM Customer WHERE userName = '" + model.userName + "' AND admin ='true'";
SqlCommand cmd = new SqlCommand(find_admin_query, conn);
conn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
//it defines admin which is true or false
model.admin = sdr.HasRows;
conn.Close();
//if admin is logged in
if (model.admin == true) {
Roles.IsUserInRole(model.userName, "admin"); //Is it right?
if (DAL.UserIsVaild(model.userName, model.password))
{
FormsAuthentication.SetAuthCookie(model.userName, true);
return RedirectToAction("Index", "Product");
}
}
//if customer is logged in
if (model.admin == false) {
if (DAL.UserIsVaild(model.userName, model.password))
{
FormsAuthentication.SetAuthCookie(model.userName, true);
return RedirectToAction("Index", "Home");
}
}
ModelState.AddModelError("", "The user name or password is incorrect.");
}
// If we got this far, something failed, redisplay form
return View(model);
}
And DAL class is
public class DAL
{
static SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["rentalDB"].ToString());
public static bool UserIsVaild(string userName, string password)
{
bool authenticated = false;
string customer_query = string.Format("SELECT * FROM [Customer] WHERE userName = '{0}' AND password = '{1}'", userName, password);
SqlCommand cmd = new SqlCommand(customer_query, conn);
conn.Open();
SqlDataReader sdr = cmd.ExecuteReader();
authenticated = sdr.HasRows;
conn.Close();
return (authenticated);
}
}
Finally, I want to make custom [Authorize(Roles="admin")]
[Authorize(Roles="admin")]
public class ProductController : Controller
{
public ViewResult Index()
{
var product = db.Product.Include(a => a.Category);
return View(product.ToList());
}
}
These are my source code now. Do I need to make 'AuthorizeAttribute' class?
If I have to do, how can I make it? Could you explain to me? I cannot understand how to set particular role in my case.
Please help me how can I do. Thanks.
I know this question is a bit old but here's how I did something similar. I created a custom authorization attribute that I used to check if a user had the correct security access:
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
// Get the roles from the Controller action decorated with the attribute e.g.
// [AccessDeniedAuthorize(Roles = MyRoleEnum.UserRole + "," + MyRoleEnum.ReadOnlyRole)]
var requiredRoles = Roles.Split(Convert.ToChar(","));
// Get the highest role a user has, from role provider, db lookup, etc.
// (This depends on your requirements - you could also get all roles for a user and check if they have the correct access)
var highestUserRole = GetHighestUserSecurityRole();
// If running locally bypass the check
if (filterContext.HttpContext.Request.IsLocal) return;
if (!requiredRoles.Any(highestUserRole.Contains))
{
// Redirect to access denied view
filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
}
}
}
Now decorate the Controller with the custom attribute (you can also decorate individual Controller actions):
[AccessDeniedAuthorize(Roles="user")]
public class ProductController : Controller
{
[AccessDeniedAuthorize(Roles="admin")]
public ViewResult Index()
{
var product = db.Product.Include(a => a.Category);
return View(product.ToList());
}
}
Your Role.IsInRole usage isn't correct. Thats what the
[Authorize(Roles="Admin")] is used for, no need to call it.
In your code you are not setting the roles anywhere. If you want to do custom role management you can use your own role provider or store them in the auth token as shown here:
http://www.codeproject.com/Articles/36836/Forms-Authentication-and-Role-based-Authorization
note the section:
// Get the stored user-data, in this case, user roles
if (!string.IsNullOrEmpty(ticket.UserData))
{
string userData = ticket.UserData;
string[] roles = userData.Split(',');
//Roles were put in the UserData property in the authentication ticket
//while creating it
HttpContext.Current.User =
new System.Security.Principal.GenericPrincipal(id, roles);
}
}
However an easier approach here is to use the built in membership in asp.net.
Create a new mvc project using the 'internet application' template and this will all be setup for you. In visual studio click on the "asp.net configuration" icon above solution explorer. You can manage roles here and assignment to roles.