I'm following this article in which is described how to assign roles to users when theiy log-in using forms authentication:
public void Application_AuthenticateRequest( Object src , EventArgs e )
{
if (!(HttpContext.Current.User == null))
{
if (HttpContext.Current.User.Identity.AuthenticationType == "Forms" )
{
System.Web.Security.FormsIdentity id;
id = (System.Web.Security.FormsIdentity)HttpContext.Current.User.Identity;
String[] myRoles = new String[2];
myRoles[0] = "Manager";
myRoles[1] = "Admin";
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(id,myRoles);
}
}
}
I put the role logic in the event handler, so I basically don't need a role provider. Nonetheless, in order to run this, appears that I must enable Role Provider in web.config. Sadly, if I just put:
<roleManager enabled="true"/>
it results in runtime errors related to a failed connection to the SQL server, like if I chose AspNetSqlRoleProvider as Role Provider.
What should I do to have roles working this way? How can I choose to use no role provider, or how should I implement a dummy one (if it makes any sense)?
You shouldn't need to enable roleManager in web.config - after all, people used to use roles with .NET 1.x before roleManager came along.
One thing that roleManager will do for you that you haven't done in your code is set Thread.CurrentPrincipal to HttpContext.Current.User. If you're relying on this (e.g. using PrincipalPermissionAttribute), then you need to add this:
Thread.CurrentPrincipal = HttpContext.Current.User;
Otherwise, I'd expect it to work: what symptoms are you seeing that makes you think it isn't working?
As for implementing a dummy RoleProvider, it's easy enough: for example see this MSDN article.
You only need to implement the GetRolesForUser and IsInRole methods; the other methods can simply throw NotSupportedException.
Related
#Roles.GetRolesForUser() in razor layout view is not returning roles. #Roles.GetRolesForUser().Count() is 0.
While #Roles.IsUserInRole('name_of_logged_in_role') returns true in the same view at the same place.
Razor View:
<p>
#User.Identity.Name //Output: MyName
#Roles.GetRolesForUser().Count() //Output: 0
#Roles.IsUserInRole("Radiologist") //Output: True
</p>
Update
#Roles.GetRolesForUser(User.Identity.Name).Length //Output: 0
#Roles.GetRolesForUser(User.Identity.GetUserName()).Length //Output: 0
After extensive research, I finally found the problem. I was able to reproduce the issue in a web application. Apparently, you cannot combine ASP.NET Identity with Simple Membership, simply like you did so with the GetRolesForUser method. The Roles object is by default setup for Simple Membership using a default provider, but it seems like your using ASP.NET Identity not Simple Membership. I didn't even notice the difference until I was wondering myself why it wasnt working.
The reason why you got string[0] is because GetRolesForUser executed an sql query to a table that doesnt exist in your database.
The reason why IsUserInRole worked was more or less it did not even use the default provider to check it used the CacheRolesInCookie
If CacheRolesInCookie is true, then roleName may be checked against the roles cache rather than the specified role provider.
So, technically it went to a connectionString that was listed by the default provider and return string[0] because you have no data in the database with that connectionString. Adding your current database to the providers would not help either because Simple Membership database schema is different from ASP.NET Identity
That being said, you should get the roles by UserName like this:
Simple Solution:
public List<string> GetRoles(string UserName)
{ List<string> roles = new List<string>();
if (!string.IsNullOrWhiteSpace(UserName))
{
ApplicationUser user = context.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
var account = new AccountController();
roles = account.UserManager.GetRoles(user.Id);
}
return roles;
}
Updated
Extended Solution:
You could extend the ASP.NET Identity Roles in your context
http://www.codeproject.com/Articles/799571/ASP-NET-MVC-Extending-ASP-NET-Identity-Roles
Today I was configuring authorization provider for Oauth middleware and trying to insert some guid value into Thread.CurrentPrincipal.Identity.Claims. But when I tried to call Thread.CurrentPrincipal's FindFirst I've got nothing.
Here is the example what I was trying to do:
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var claimsIdentity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
if (claimsIdentity != null)
claimsIdentity.AddClaim(new Claim("TestClaim", Guid.NewGuid().ToString()));
var claimValue = ((ClaimsPrincipal)Thread.CurrentPrincipal)
.FindFirst(x => x.Type == "TestClaim"); //claimValue == null!
}
Checking inner properties, found that Thread.CurrentPrincipal.Identity still contains claim I've set before, but Thread.CurrentPrincipal.Identities[0] - doesn't. So there are two different identity instances with their own set of claims.
I tried to do the same steps inside Web Api controller's action and there Identity was referencing to Identities[0] which means that there is the same instance.
What is happening to OWIN middleware's Currentprincipal so it's Identity and Identities[0] refer to different instances? Can anyone explain me this, please?
Thank you!
I met the same issue. I don't know why the Identity property and the first identity of the Identities property are different instances...
But it seems that all methods relative to claims in the ClaimsPrincipal class (Claims, FindFirst...) are based on the Identities property, so updating the Identity property has no effect.
I prefer to keep the two identities consistent, so I use the following workaround to solve the problem :
principal = (ClaimsPrincipal)Thread.CurrentPrincipal
identity = (ClaimsIdentity)user.Identity;
identity1 = user.Identities.First();
identity.AddClaim(claim);
identity1.AddClaim(claim);
I'm using the Forms Auth and ASP Universal Membership Provider in an MVC 3 Site. We're persisting the cookie for user convenience.
FormsAuthentication.SetAuthCookie(model.UserName, true)
When we disable a user in the Membership provider like this:
memUser.IsApproved = model.IsActive;
provider.UpdateUser(memUser);
if they have the cookie they can still get in to the site. This is similar to what is described in this post:http://stackoverflow.com/questions/5825273/how-do-you-cancel-someones-persistent-cookie-if-their-membership-is-no-longer-v
We use Authorize attributes on our controllers, and I know that that is technically more Authorize than Authentication. But the certainly overloap so I'm trying to figure out what is the best MVC way to do a check that the user is not actually disabled? Custom AuthorizeAttribute that checks the user against the membership database? An obvious setting/method I'm missing with Forms auth to invalidate the ticket?
Update:
Here’s basically what I'm going with – we use a custom permission denied page which we we use to better inform user that they don’t have rights vs. they’re not logged in. And I added the IsApproved check. AuthorizeCore gets called when you put the attribute on a Controller or Action and if it returns false HandleUnauthorizedRequest is called.
public class CustomAuthorization : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated || !Membership.GetUser(filterContext.HttpContext.User.Identity.Name).IsApproved)
{
filterContext.Result = new HttpUnauthorizedResult();
// in the case that the user was authenticated, meaning he has a ticket,
// but is no longer approved we need to sign him out
FormsAuthentication.SignOut();
}
else
{
var permDeniedRouteVals = new System.Web.Routing.RouteValueDictionary() { { "controller", "MyErrorController" }, { "action", "MyPermissionDeniedAction" }, { "area", null } };
filterContext.Result = new RedirectToRouteResult(permDeniedRouteVals);
}
}
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
// since persisting ticket,
// adding this check for the case that the user is not active in the database any more
return base.AuthorizeCore(httpContext) && Membership.GetUser(httpContext.User.Identity.Name).IsApproved;
}
}
Usage:
[CustomAuthorization()]
public class MyController
Well, you're going to have to check the database regardless, the only question is how you want to do that. Yes, you could create a custom authorize attribute, or you could write some code for the OnAuthorize override in ControllerBase, or you could do it in Application_AuthenticateRequest.. lots of ways you could do it, depends on what works best for you.
The best way, of course, would be to not use a persistent ticket if this is an issue for you.
I pretty much always use Roles and a RolesProvider, even if there is just one role named "Login" - in part for this issue. This way, your Authorize attributes might look something like this:
[Authorize(Roles="Login")]
Where Login represents a basic 'Role' that all "active" accounts must have to be able to log in at all; Every protected action is protected by this, at minimum.
That way, simply removing the "Login" role effectively disables the user... because, in my Roles Provider, I am checking the logged-in user's roles against the database or server-local equivalent.
In your case, your "Login" role could simply resolve to a check on the IsApproved field on your user model.
I have a legacy UserID (int32) which I wish to link to asp.net membership. I have set up link tables on the database and I'm happy with that part. The question is where to store the UserID in the web application so that it is easily accessible when required.
I decided that the best place to store it is in the UserData part of the FormsAuthenticationTicket in the LoggedIn event of the Login Control. My first attempt to make this accessible was to extract it in the PreInit of my BasePage class. The trouble with this is that it gets messy when the UserID is required by UserControls.
Is it acceptable to just wrap it in a static method or property in a Utilities class, something like this:
public static int UserID
{
get
{
int userID = 0;
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
userID = Int32.Parse(ticket.UserData);
}
return userID;
}
}
This seems to work but I don't know if I'm breaking some unwritten rule here. I presume all this stuff is in memory so there's no great overhead in this access.
Your code looks fine from a functional perspective (though I would clean it up a bit, but that's more a style thing).
You might consider making it an extension method, though, rather than just sticking it in a random utility class. Maybe an extension for the IIdentity class?
int myUserId = HttpContext.Current.User.Identity.MyUserId();
Using the UserData field is fine, I guess. Another option is to create your own IIdentity object with a custom Ticket and wrap them in a GenericPrincipal -- might be too much work for what you're after, though.
I'm building my site, and I want to restrict a part of my site (The admin parts) from normal public display.
I am using LINQ for database access.
I have a Service class to handle calls to the database through LINQ
I have the whole site running, except for the Login part.
So far I have only been able to find examples using MembershipProvider and/or RoleProviders etc. And to be honest, it does seem like too much work for what I want. All this has to do is to let you in if you type the correct password in the input fields.
Can i really not avoid the Providers?
Since you only have a single user you don't need to create a database dependency. You can make a very simple authorization service based off of a hard coded credentials. For example,
public class AuthorizationService{
private AuthorizationService(){}
public static readonly AuthorizationService Instance = new AuthorizationService();
private const string HardCodedAdminUsername = "someone";
private const string HardCodedAdminPassword = "secret";
private readonly string AuthorizationKey = "ADMIN_AUTHORIZATION";
public bool Login(string username, string password, HttpSessionStateBase session){
if(username.ToLowerInvariant().Trim()==HardCodedAdminUsername && password.ToLowerInvariant().Trim()==HardCodedAdminPassword){
session[AuthorizationKey] = true;
return true;
}
return false;
}
public void Logout(HttpSessionStateBase session){
session[AuthorizationKey] = false;
}
public bool IsAdmin(HttpSessionStateBase session){
return session[AuthorizationKey] == true;
}
}
Then you can build a custom IAuthorizationFilter like:
public class SimpleAuthFilterAttribute: FilterAttribute, IAuthorizationFilter{
public void OnAuthorization(AuthorizationContext filterContext){
if(!AuthorizationService.Instance.IsAdmin(filterContext.HttpContext.Session)){
throw new UnauthorizedAccessException();
}
}
}
Then all you have to do is decorate the protected controller actions with the SimpleAuthFilter and you're application's login suddenly works. Yay! (Note, I wrote all this code in the StackOverflow answer window, so you may need to clean up typos, etc. before it actually works)
Also, you could refactor this to omit the username if you find that unnecessary. You will need to create a controller action for Login and Logout that make the corresponding calls to the AuthorizationService, if you want your protected controller actions to ever be accessible.
Its worth building a light-weight Membership Provider with minimal implementation; GetUser, ValidateUser etc methods. YOu dont need to implement the whole thing. It just helps with authorising pages and checking User.Identity etc when needed. You also dont need the RoleProvider or ProfileProvider to do this.
Its also scalable for the future.
UPDATE
You just need to implement the core methods to valudate and get the user and insert your own validation/data access code.
Something like this....
web.config settings:
<membership defaultProvider="ApplicationMembershipProvider">
<providers>
<clear/>
<add name="ApplicationMembershipProvider" type="YourNamespace.ApplicationMembershipProvider"/>
</providers>
</membership>
Login Code:
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
}
You can set the status (logged in or not) in a session variable. Set the variable to true if the user entered the correct password, then on every page you want to restrict access, check if the variable is true.
#KristianB a while ago I gave an answer to this SO question. I believe it may be useful since it's very straightforward to implement and at the same time it's better than hardcoding a username and a password in your code.
Good luck!