I'm using windows authentication and custom roles. I've extended the WindowsPrincipal because I want to include additional information about the user based on a User class I've added. When I run the application, it sees the CustomPrincipal assigned to the built-in User, but not the additional "user" property I've added. I'm sure I'm doing something really dumb, but this is my first run into the C# ASP world and could really appreciate some help. Here is my custom principal and global.asax
Custom principal:
public class CustomPrincipal : WindowsPrincipal
{
List<string> _roles;
private User thisUser = new User();
public CustomPrincipal(WindowsIdentity identity)
: base(identity)
{
_roles = new List<string>(Roles.GetRolesForUser());
user = thisUser.getDarUser(identity.Name);
}
public User user { get; private set; }
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role) || _roles.Contains("Admin") || _roles.Contains("Dev"))
{
return true;
}
else
{
return false;
}
}
}
And Global.asax:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
IIdentity thisId = User.Identity;
WindowsIdentity wi = (WindowsIdentity)thisId;
CustomPrincipal cp = new CustomPrincipal(wi);
HttpContext.Current.User = cp;
Thread.CurrentPrincipal = cp;
}
}
Thanks again for any direction.
Looking into it a little more I found the fail was in how I was trying to access the principal in my views. Thanks to ASP.NET MVC - Set custom IIdentity or IPrincipal, while this was not related to my Windows Auth type project, it did lead me to the correct usage of my principal.
What I was doing WRONG:
#User.user.myproperty
Change to:
#((User as CustomPrinicpal).user.myproperty
Hopefully this helps another newb not make the same bonehead mistake
Related
I am creating an Asp.NET MVC 5 application. For this project, I am trying to implement a custom authentication mechanism (i don't want to use Forms Authentication / OWIN etc. external providers)
I created a custom authorize attribute as follows:
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class myAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!HttpContext.Current.Request.IsAuthenticated)
{
httpContext.Response.Redirect("~/Account/Login");
}
return base.AuthorizeCore(httpContext);
}
}
And in my Login action, I am trying to change the value of
HttpContext.User.Identity.IsAuthenticated
But it is read only and I am unable to change the value. Can I change its value manually or am I making a logical mistake.
You can achieve this by manually settings HttpContext.User:
var identity = new ClaimsIdentity("Custom");
HttpContext.User = new ClaimsPrincipal(identity);
It's important to set a custom authenticationType. In the example above, I just used the string "Custom", but it can be anything you want.
With that, HttpContext.User.Identity.IsAuthenticated will be true.
For something more complicated, you can add claims like this:
var identity = new ClaimsIdentity(new List<Claim>
{
new Claim("UserId", "123", ClaimValueTypes.Integer32)
}, "Custom");
HttpContext.User = new ClaimsPrincipal(identity);
This results in:
HttpContext.User.Identity.IsAuthenticated == true;
int.Parse(((ClaimsIdentity)HttpContext.User.Identity).ValueFromType("UserId")) == 123;
My answer may no suit you perfectly but it may help.
In my ASP.NET Core MVC application, administrators need to impersonate other users.
It is an intranet application and obviously users are authenticated by Windows authentication.
It is done thanks to an ajax request to this controller action :
public async Task<JsonResult> UserImpersonation(IdentityExtension userIdentity)
IdentityExtension is a custom class of which you can observe the signature below :
public class IdentityExtension : IIdentity
{
public IdentityExtension()
{ }
public IdentityExtension(string name)
{
this.Name = name;
}
public string AuthenticationType => "Kerberos";
public bool IsAuthenticated => true;
public string Name { get; set; }
}
UserImpersonation method returns the success state of ReplaceUser method, wich updates HttpContext.User this way :
this.HttpContext.User = identity as ClaimsPrincipal;
return true;
identity being an instance of IdentityExtension.
I hope my solution can be adapted to your use case !
We can see, from the source code:
/// <summary>
/// Gets a value that indicates if the user has been authenticated.
/// </summary>
public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(_authenticationType); }
}
so, it means when we have a null _authenticationType, IsAuthenticated will always be false;
otherwise, it will be true.
If you want the HttpContext to persist and be able to fetch claims afterward, do the following. I'll be using Cookie Authentication as the authenticationType.
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.Name, TempData["Username"].ToString()));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
you can't use FormsAuthentication.SetAuthCookie?
https://learn.microsoft.com/en-us/dotnet/api/system.web.security.formsauthentication.setauthcookie?view=netframework-4.7.2
I'm looking for a way to program a custom authorization filter in ASP.NET 5 as the current implementation relies in Policies/Requirements which in turn rely solely in the use of Claims, thus on the umpteenth and ever-changing Identity System of which I'm really tired of (I've tried all it's flavors).
I have a large set of permissions (over 200) which I don't want to code as Claims as I have my own repository for them and a lot faster way to be check against it than comparing hundreds of strings (that is what claims are in the end).
I need to pass a parameter in each attribute that should be checked against my custom repository of permissions:
[Authorize(Requires = enumPermission.DeleteCustomer)]
I know this is not the most frequent scenario, but I think it isn't an edge case. I've tried implementing it in the way described by #leastprivilege on his magnificent post "The State of Security in ASP.NET 5 and MVC 6: Authorization", but I've hit the same walls as the author, who has even opened an issue on the ASP.NET 5 github repo, which has been closed in a not too much clarifying manner: link
Any idea of how to achieve this? Maybe using other kind of filter? In that case, how?
Following is an example of how you can achieve this scenario:
Let's assume you have a service called IPermissionStore which validates if a given user has the required permissions specified on the attribute.
public class MyCustomAuthorizationFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
private readonly Permision[] _permissions;
public MyCustomAuthorizationFilterAttribute(params Permision[] permissions)
{
_permissions = permissions;
}
public int Order { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var store = serviceProvider.GetRequiredService<IPermissionStore>();
return new MyCustomAuthorizationFilter(store, _permissions)
{
Order = Order
};
}
}
public class MyCustomAuthorizationFilter : IAuthorizationFilter, IOrderedFilter
{
private readonly IPermissionStore _store;
private readonly Permision[] _permissions;
public int Order { get; set; }
public MyCustomAuthorizationFilter(IPermissionStore store, params Permision[] permissions)
{
_store = store;
_permissions = permissions;
}
public void OnAuthorization(AuthorizationContext context)
{
// Check if the action has an AllowAnonymous filter
if (!HasAllowAnonymous(context))
{
var user = context.HttpContext.User;
var userIsAnonymous =
user == null ||
user.Identity == null ||
!user.Identity.IsAuthenticated;
if (userIsAnonymous)
{
Fail(context);
}
else
{
// check the store for permissions for the current user
}
}
}
private bool HasAllowAnonymous(AuthorizationContext context)
{
return context.Filters.Any(item => item is Microsoft.AspNet.Authorization.IAllowAnonymous);
}
private void Fail(AuthorizationContext context)
{
context.Result = new HttpUnauthorizedResult();
}
}
// Your action
[HttpGet]
[MyCustomAuthorizationFilter(Permision.CreateCustomer)]
public IEnumerable<string> Get()
{
//blah
}
I have written a custom PrincipalPermissionAttribute that uses an AuthenticationService instead of Thread.CurrentPrincipal as PrincipalPermissionAttribute does.
It works as I like, but if the user logs out and back in, or if the user's roles were to change, the attribute code is never called a second time. I suspect I've not informed the attribute it needs to recheck the permission? Breakpoint set on the CreatePermission method only ever hits once.
Is an attribute code only ever evaluated once? The attribute is currently decorating an event handler for a button click on my View's codebehind.
If I change my method back to use the PrincipalPermissionAttribute then it does work as I expect, logging out and back in as a user without the right role throws the SecurityException I expect. Have I missed overriding a property perhaps?
[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class RolePermissionAttribute : CodeAccessSecurityAttribute
{
private readonly PrincipalPermission _revoke = new PrincipalPermission(PermissionState.None);
private readonly PrincipalPermission _allow = new PrincipalPermission(PermissionState.Unrestricted);
private IList<string> _roles;
private readonly IAuthenticationService _authorisationService;
public RolePermissionAttribute(SecurityAction action)
: this(action, ServiceLocator.Current.GetInstance<IAuthenticationService>())
{
}
public RolePermissionAttribute(SecurityAction action, IAuthenticationService authorisationService)
: base(action)
{
_authorisationService = authorisationService;
}
public string Roles { get; set; }
public bool Authenticated { get; set; }
public override IPermission CreatePermission()
{
_roles = (this.Roles ?? string.Empty).Split(',', ';')
.Select(s => s.Trim())
.Where(s => s.Length > 0)
.Distinct()
.ToList();
bool result = false;
if (_authorisationService != null)
{
var principal = _authorisationService.ClientSecurityPrincipal;
if (principal == null)
{
throw new SecurityException("Access Denied. You are not logged in");
}
// If Authenticated is enforced then revoke if user is not authenticated
if (Authenticated && !_authorisationService.IsAuthenticated)
{
throw new SecurityException("Access Denied. You are not authenticated");
}
// Allow if the principal is in any of the roles
result = _roles.Any(principal.IsInRole);
if (!result)
{
throw new SecurityException("Access Denied. You are not in an allowed Role");
}
}
return result ? _allow : _revoke;
}
}
}
Here's the method with the attribute
[RolePermission(SecurityAction.Demand, Authenticated = true, Roles = "Admin")]
private void barButtonItemConfig_ItemClick(object sender, ItemClickEventArgs e)
{
// Do stuff
}
Ok I've figured out how it works. CreatePermission is actually only called once. The IPermission that is returned is the class that checks if the user is in the required role or not.
Because I was returning an unrestricted allow for user A, user B got the same access regardless of their roles.
I need to create my own class that implements IPermission and move my logic into the Demand method. Alternatively (the easier option) assign the Principal from my service to Thread.CurrentPrincipal and use the out of the box PrincipalPermissionAttribute.
I've written a custom principal object which contains a few additional fields (email and userid in addition to the username).
In order to access these properties I have to cast the Context.User object as my custom principal.
#Html.GetGravitarImage((User as CustomPrincipal).Email)
This custom principal is created / deserialized via the Application_AuthenticateRequest in my global.ascx. You can see this question I asked here for more information.
private void Application_AuthenticateRequest(Object source, EventArgs e)
{
var application = (HttpApplication)source;
var context = application.Context;
// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = context.Request.Cookies[cookieName];
if (authCookie == null)
return;
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
context.User = CustomPrincipal.CreatePrincipalFromCookieData(authTicket.UserData);
}
However, if a user isn't authenticated, then my cast to CustomPrincipal will fail (because it won't be injected in the method above) and the result of the (User as CustomPrincipal) will return null, thus giving me a null reference exception when my method above attempts to get the email.
What would be a clean solution to this problem? I want to make accessing my custom principal easy and having to do the following seems cumbersome:
#Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")
Is this the only way to handle this situation?
I whipped something together quickly. One possible way of easily introducing a custom IPrincipal in ASP.NET MVC is the following:
1) Create your own descendant of the IPrincipal interface.
public interface IMyPrincipal : IPrincipal
{
Guid UserId { get; }
string EmailAddress { get; }
}
2) Let's assume you are using the ASP.NET Membership provider to authenticate your users. Let's quickly build an IMyPrincipal implementation which utilizes the membership API.
public class MyPrincipal : IMyPrincipal
{
private MembershipUser _user;
public MyPrincipal()
{
this._user = Membership.GetUser();
var userName = this._user != null ? this._user.UserName : String.Empty;
this.Identity = new GenericIdentity(userName);
}
public Guid UserId
{
get
{
return this._user != null ? (Guid) this._user.ProviderUserKey :
default(Guid);
}
}
public string EmailAddress
{
get
{
return this._user != null ? this._user.Email : null;
}
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
}
3) Create your own base class type for your controllers. Hide the inherited User member and introduce your own IPrincipal descendant.
public class BaseController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
4) Have all your controllers descend from this new BaseController type.
public class HomeController : BaseController
{
//...
}
5) Create your own controller factory to make sure your principal is introduced on the HttpContext / Thread.
public class MyControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance
(RequestContext requestContext, Type controllerType)
{
try
{
var controller = base.GetControllerInstance(requestContext, controllerType);
requestContext.HttpContext.User = Thread.CurrentPrincipal = new
MyPrincipal();
return controller;
}
catch (Exception)
{
return base.GetControllerInstance(requestContext, controllerType);
}
}
}
6) Register the controller factory in the Global.asax's Application_Start() event handler.
var controllerFactory = new MyControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
Voila, now you can use the new User (IMyPrincipal) anywhere in your controllers.
For example:
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.UserId = User.UserId;
ViewBag.UserName = User.EmailAddress;
return View();
}
You could either create a base class and override the "User" property using the "new" keyword or create an extension method like this:
public static class ControllerExtensions
{
public static CustomPrincipal CustomPrincipal(this Controller controller)
{
if(controller.User is CustomPrincipal)
{
return controller.User as CustomPrincipal;
}
return null; // maybe return an empty object instead to get around null reference...
}
}
The best way to make your IPrincipal implementation accessible in your Razor pages using ASP.NET MVC, is doing the following:
Implement the System.Security.Principal.IPrincipal interface.
Implement the System.Security.Principal.IIdentity interface.
In Global.asax define a method for: void Application_AuthenticateRequest(Object, EventArgs) that persists your both implementations of IPrincipal and IIdentity.
Create an extension method for IPrincipal to expose your implementation of IIdentity.
Finally, add the namespace for the previous extension method in your web.config file in <system.web.webPages.razor>.
At the end, you will be able to access your custom implementation of IIdentity instead of type casting. You now can access your custom implementation like this:
Hello #User.CustomIdentity().FirstName #User.CustomerIdentity().LastName!
These steps are a concise and brief description of a well detailed article written here: http://rizzo7.blogspot.com/2012/04/mvc-30-razor-custom-principal-and.html
You could create some sort of utility method or add a method to one of your services that checks if it's your custom principal. Maybe something like:
public class UserService : IUserService
{
public CustomPrincipal CurrentUser
{
get
{
CustomPrincipal user = HttpContext.Current.User as CustomPrincipal;
if (user == null)
return GuestUser; // Just some default user object
return user;
}
}
}
You could also make extension methods for the Email and UserID in the same fashion as John Kalberer's answer:
public static class CustomPrincipalExtensions
{
public static string Email(this CustomPrincipal cUser)
{
return cUser != null ? cUser.Email : "Default Email"
}
public static string UserID(this CustomPrincipal cUser)
{
return cUser != null ? cUser.UserID : "Default ID"
}
}
When not authorized, you could set the user object to a specific instance of the custom principal with default values:
if (authCookie == null)
{
context.User = CustomPrincipal.Default; // Or CreateDefault()
return;
}
I have a couple of table on my database that specify witch users ( Depending on your AD Username) can actually use the current ASP.NET MVC 2 app I'm building.
My question is how ( or more likely where and where do I put it? On the master page?? ) do i write a method that gets the AD user out of the HTTP context and validates it against the database to see if you can actually use the app? If you can... the idea it's to write a couple of keys in the Session object with the information I need ( Role, Full Name, etc ).
I'm quite confused regarding how I should accomplish this and if it's actually the right way... Keep in mind that I have an admin section and non-admin section in my app.
Any thoughts?
Edit: Keep in mind that I do not care to authenticate the user through a form. All I want to check is if according to my database and your AD username you can use my app. If you can write to session in order to perish the information I need. Otherwise just throw an error page.
This is what I've implemented so far, is this the way to go?
What's the second method for? ( I'm sorry I'm kind of new to c#) What I want to do it's actually throw a view if yo're not authorized...
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var canUse = this._userRepo.CanUserUseApp(httpContext.User.Identity.Name);
if (!canUse)
{
isAuthorized = false;
}
}
return isAuthorized;
}
You could activate and use Windows (NTLM) authentication and then write a custom [Authorize] attribute where you could fetch the currently connected AD user and perform the additional check of whether he is authorized or not to use the application against your data store. Then you would decorate controllers/actions that require authorization with this custom attribute.
UPDATE:
Here's an example of how such custom attribute might look like:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
// The user is authorized so far => check his credentials against
// the custom data store
return IsUserAllowedAccess(httpContext.User.Identity.Name);
}
return isAuthorized;
}
private bool IsUserAllowedAccess(string username)
{
throw new NotImplementedException();
}
}
and then:
[MyAuthorize]
public class FooController: Controller
{
public ActionResult Index()
{
...
}
}
Create a class called AdminAttribute with this code
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AdminsAttribute : AuthorizeAttribute
{
public AdminsAttribute()
{
this.Roles = "MSH\\GRP_Level1,MSH\\Grp_Level2";
}
}
public class HomeController : Controller
{
[Admins]
public ActionResult Level1()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}