In a custom role provider (inheriting from RoleProvider) in .NET 2.0, the IsUserInRole method has been hard-coded to always return true:
public override bool IsUserInRole(string username, string roleName) { return true; }
In an ASP.NET application configured to use this role provider, the following code returns true (as expected):
Roles.IsUserInRole("any username", "any rolename"); // results in true
However, the following code returns false:
Roles.IsUserInRole("any rolename"); // results in false
Note that User.IsInRole("any rolename") is also returning false.
Is this the expected behavior?
Is it incorrect to assume that the overload that only takes a role name would still be invoking the overridden IsUserInRole?
Update: Note that there doesn't seem to be an override available for the version that takes a single string, which has led to my assumption in #2.
I looked at Roles.IsUserInRole(string rolename) in .net reflector, and it resolves to the following:
public static bool IsUserInRole(string roleName)
{
return IsUserInRole(GetCurrentUserName(), roleName);
}
I would take a look at your current user. Here's why:
private static string GetCurrentUserName()
{
IPrincipal currentUser = GetCurrentUser();
if ((currentUser != null) && (currentUser.Identity != null))
{
return currentUser.Identity.Name;
}
return string.Empty;
}
I would be willing to bet this is returning an empty string because you either don't have a Current User, or its name is an empty string or null.
In the IsUserInRole(string username, string roleName) method, there is the following block of code right near the beginning:
if (username.Length < 1)
{
return false;
}
If your GetCurrentUserName() doesn't return anything meaningful, then it will return false before it calls your overridden method.
Moral to take away from this: Reflector is a great tool :)
Also beware if you have selected cacheRolesInCookie="true" in the RoleManager config. If you have added a new role to the database, it might be looking at the cached version in the cookie.
I had this problem and the solution was to delete the cookie and re-login.
This may help someone - be aware:
If you are using the login control to authenticate - the username entered into the control becomes the HttpContext.Current.User.Identity.Name which is used in the Roles.IsUserInRole(string rolename) and more specifically - the membership's GetUser() method. So if this is the case make sure you override the Authenticate event, validate the user in this method and set the username to a value that your custom membership provider can use.
protected void crtlLoginUserLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
bool blnAuthenticate = false;
string strUserName = crtlLoginUserLogin.UserName;
if (IsValidEmail(strUserName))
{
//if more than one user has email address - must authenticate by username.
MembershipUserCollection users = Membership.FindUsersByEmail(strUserName);
if (users.Count > 1)
{
crtlLoginUserLogin.FailureText = "We are unable to determine which account is registered to that email address. Please enter your Username to login.";
}
else
{
strUserName = Membership.GetUserNameByEmail(strUserName);
blnAuthenticate = Membership.ValidateUser(strUserName, crtlLoginUserLogin.Password);
//setting the userLogin to the correct user name (only on successful authentication)
if (blnAuthenticate)
{
crtlLoginUserLogin.UserName = strUserName;
}
}
}
else
{
blnAuthenticate = Membership.ValidateUser(strUserName, crtlLoginUserLogin.Password);
}
e.Authenticated = blnAuthenticate;
}
Related
I am working on a project that uses an ASP.NET MVC application which has a page on there that only certain users should be able to access. Using Windows Authentication, I want to take the User.Identity.Name and check that against the LogonID field in my Users table in the database. If there is a match, I then want to check if the IsAdmin field equals true and if so, grant access to the desired page.
I am fairly new to this so I was wondering how I would need to go about it?
UPDATE:
So i've tried to use the AuthorizeAttribute which has been suggested to me but I have come across a problem.
I am using a SQL Server Compact Database without a DBContext. So I was wondering how I would write my entity in order to access the database?
public class AuthorizeAuthorAttribute : AuthorizeAttribute
{
//Entity to access Database
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
return false;
}
string currentUser = httpContext.User.Identity.Name;
var userName = //Linq statement
string my = userName.ToString();
if (currentUser.Contains(my))
{
return true;
}
else
{
return false;
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
You could implement custom Authorize filter and decorate the required controller with this filter. Inside custom filter Authorize core method, check the windows identity against your database and return true/false accordingly.
https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.authorizecore(v=vs.118).aspx
Just wondering what the best way to store user login data on successful login in my application. I.e. when logged in at the moment I do something like this in my login script
Session("loggedIn") = "Yes"
Session("userName") = reader_login("useremail").ToString()
Session("userId") = reader_login("user_ID").ToString()
Session("firstName") = reader_login("firstName").ToString()
Session("lastName") = reader_login("lastName").ToString()
And then I use these session variables on my scripts, but just noticed that every time I want to use some of these session variables I do need to check if they are nut null before calling them, seems a bit clumsy to repeat these for a lot of my .aspx pages. Is there a better way to do this ?
EDIT :
Also I am wondering why do I need to add the IS NUll check for the session on each script I use session variables I put the check in the master page but noticed I still get null exception in my usercontrol which is referenced in my master page but does not have the IS null check
Session is not the way to check whether user is authenticated or not. Session may be cleared on demand by administrator when clearing app pool, or by the low memory on server. You won't wish to log out user in such cases. The builtin and reccommended way for doing this in ASP.NET is storing data in authentication cookie. Once the user is logged in, you issue the cookie that contains all the data, including user id, name, etc. And then, you don't have to check every property in session for null, more simple - you just check if the user is authenticated - then you've got the data, else -not. And the other benefit, if you substitute builtin principal with custom one, you can define strongly typed object that holds user data, no more casting from objects extracted from session. Here're the examples for defining custom principal with forms authentication
First, let's define custom MyIdentity and MyPrincipal
public class MyIdentity : IIdentity
{
private FormsAuthenticationTicket _Ticket;
private int _userId = 0;
public FormsAuthenticationTicket Ticket
{
get { return _Ticket; }
}
public string Name
{
get { return _Ticket.Name; }
}
public int UserId
{
get
{
if (_userId == 0)
_userId = Convert.ToInt32(_Ticket.UserData.Split("|".ToCharArray())[0]);
return _userId;
}
}
public Identity(FormsAuthenticationTicket ticket)
{
this._Ticket = ticket;
}
public string AuthenticationType
{
get { return "Custom"; }
}
public bool IsAuthenticated
{
get { return UserId > 0; }
}
}
Then the MyPrincipal that holds MyIdentity
public class MyPrincipal : IPrincipal
{
private MyIdentity _Identity;
public Principal(MyIdentity identity)
{
_Identity = identity;
}
public IIdentity Identity
{
get { return _Identity; }
}
public bool IsInRole(string role)
{
return false;
}
}
Then substitute original forms user with the custom one. In Global.asax
private void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
IPrincipal usr = HttpContext.Current.User;
// If we are dealing with an authenticated forms authentication request
if (usr.Identity.IsAuthenticated && usr.Identity.AuthenticationType == "Forms")
{
FormsIdentity formsIdentity = usr.Identity as FormsIdentity;
// Create a CustomIdentity based on the FormsAuthenticationTicket
IIdentity identity = new MyIdentity(formsIdentity.Ticket);
IPrincipal principal = new MyPrincipal(identity);
// Attach the CustomPrincipal to HttpContext.User and Thread.CurrentPrincipal
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
}
}
Define method for issuing forms authentication ticket. Later, the custom MyIdentity class will extract userId and other methods from userData.
public static HttpCookie GetAuthCookie(string userName, string userData, bool createPersistentCookie, HttpSessionStateBase session)
{
HttpCookie authCookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData, session.SessionID);
authCookie.Value = FormsAuthentication.Encrypt(newTicket);
return authCookie;
}
When the user is checked and is authenticated, return them authentication cookie
Response.Cookies.Add(AuthenticationCookie.GetAuthCookie(model.UserName, GetUserInfo(model.UserName, passwordHash), model.RememberMe, Session));
//GetUserInfo returns | separated string of user datas. "userId|userName|firstName|lastName" for example.
And at last, using all of the above in code
if(User.Identity.IsAuthenticated)
{
int userId = ((MyIdentity)User.Identity).UserId;
}
This may seem the larger code, but in runtime it'll give much more benefits than storing all the data in session. The main of them are null checking and casting every time.
You could load this through a single object which you put in the Session. This will remove all your strings as you can just set properties. Also you can check if the object is available in the session, if it's not the user is not logged in?
public class CurrentUserObject
{
public string UserName { get; set; }
public string UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public CurrentUserObject(string userName, string userID, string firstName, string lastName)
{
UserName = userName;
UserID = userID;
FirstName = firstName;
LastName = lastName;
}
}
You can instantiate this object and store it in Session("CurrentUser") or something. If you request this session variable and it turns out to be null, your user is not logged in. I would advise you to do this in a master page or something by the way to avoid duplication of this code.
you don't have to store "loggedIn" in session.
you can use Session["userName"] to check, if it is null, not logged in; not null, logged in.
try to use one session item to track user login status, such username or userid.
also you can encapsule the logic into a method such as
static bool CheckLogin(HttpSession sessionState, out username, out userId, out firstName, out LastName);
FYI
may be you need to use caching in your application because you are going to check if null or not every time i think for save use data caching will be better and here are some links :
http://msdn.microsoft.com/en-us/library/xsbfdd8c(v=vs.71).aspx
http://msdn.microsoft.com/en-us/library/ms972379.aspx
http://www.exforsys.com/tutorials/asp.net/caching-in-asp.net.html
http://www.codeproject.com/KB/web-cache/cachingaspnet.aspx
Hope it helps mark as answered if it helps :)
I am having a hard time implementing "Remember Me" functionality in an MVC application with a custom principal. I have boiled it down to ASP.NET not retrieving the authentication cookie for me. I have included a snapshot below from Google Chrome.
Shows the results of Request.Cookies that is set within the controller action and placed in ViewData for the view to read. Notice that it is missing the .ASPXAUTH cookie
Shows the results from the Chrome developer tools. You can see that .ASPXAUTH is included here.
What may be the issue here? Why does ASP.NET not read this value from the cookie collection?
My application uses a custom IPrincipal. BusinessPrincipalBase is a CSLA object that ust implements IPrincipal. Here is the code for that:
[Serializable()]
public class MoralePrincipal : BusinessPrincipalBase
{
private User _user;
public User User
{
get
{
return _user;
}
}
private MoralePrincipal(IIdentity identity) : base(identity)
{
if (identity is User)
{
_user = (User)identity;
}
}
public override bool Equals(object obj)
{
MoralePrincipal principal = obj as MoralePrincipal;
if (principal != null)
{
if (principal.Identity is User && this.Identity is User)
{
return ((User)principal.Identity).Equals(((User)this.Identity));
}
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool Login(string username, string password)
{
User identity = User.Fetch(username, password);
if (identity == null || !identity.IsAuthenticated)
{
identity = (User)User.UnauthenicatedIdentity;
}
MoralePrincipal principal = new MoralePrincipal(identity);
Csla.ApplicationContext.User = principal;
Context.Current.User = identity;
return identity != null && identity.IsAuthenticated;
}
public static void Logout()
{
IIdentity identity = User.UnauthenicatedIdentity;
MoralePrincipal principal = new MoralePrincipal(identity);
ApplicationContext.User = principal;
Context.Current.User = identity as User;
}
public override bool IsInRole(string role)
{
if (Context.Current.User == null || Context.Current.Project == null)
{
return false;
}
string userRole = Context.Current.User.GetRole(Context.Current.Project.Id);
return string.Compare(role, userRole, true) == 0;
}
The application also uses a custom membership provider. Here is the code for that.
public class MoraleMembershipProvider : MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
bool result = MoralePrincipal.Login(username, password);
HttpContext.Current.Session["CslaPrincipal"] = ApplicationContext.User;
return result;
}
#region Non-Implemented Properties/Methods
public override string ApplicationName
{
get
{
return "Morale";
}
set
{
throw new NotImplementedException();
}
}
// Everything else just throws a NotImplementedException
#endregion
}
I do not think that any of this is related because the bottom line is that the Request.Cookies does not return the authentication cookie. Is it related to the size of the cookie? I heard there are issues to the size of the cookie.
UPDATE: It seems that the issue revolves around subdomains. This site was being hosted with a subdomain and the cookie domain was left blank. Does anyone have any pointers on how I can get the auth cookie to work with all domains (e.g. http://example.com, http://www.example.com, and http://sub.example.com)?
If you are trying to store the actual User object in the cookie itself, it is probably too big to store as a cookie. I am not too familiar with the MVC authentication stuff, but in web forms I generally do the following:
FormsAuthentication.RedirectFromLoginPage(user_unique_id_here, false);
The second parameter is for the persistency you are looking for.
From there I create a custom context (UserContext) that I populate via HttpModule that gives me access to all the user and role information.
Since I do not develop in MVC (yet) or CSLA, I'm not sure how much more help I can be. If I were you, I would also ditch the custom membership provider. You might as well just call MoralePrincipal.Login directly in your Authentication controller.
The rememberMe stuff should be set by the FormsAuthenticationService (in MVC2) or the FormsAuthentication static class in MVC1, if you're using the 'regular' AccountController's code. If you changed that code, did you remember to add in the (optional) boolean param indicating whether to use a persistent cookie or not?
It sounds to me like you're getting a session cookie, but not a persistent cookie.
I am writing my own custom Identity class which implements IIdentity. I don't need to change the default method IsAuthenticated but so now I was wondering how does the default IIdentity determines if it should return true or false?
I thought to find the answer in the FormsAuthenticationTicket I am using but not sure if that is correct.
Thanks in advance,
Pickels
There is no 'default IIdentity' in the context of an ASP.Net handler.
There is a GenericIdentity that is pass to a GenericPrincipal which is the default User for an ASP.Net handler, and it's behavior is that if it is instantiated with a non-empty username then it is authenticated.
e.g.
public virtual bool IsAuthenticated
{
get
{
return !this.m_name.Equals("");
}
}
That said, the determination of IsAuthenticated is completely arbitrary and the class implementing IIdentity is fully responsible for implementing this logic.
Typically, there is no use case for instantiating an un-authenticated principal/identity as this is done automatically by the asp.net runtime, thus implementing your custom IIdentity with a 'dumb' IsAuthenticated that returns true should be appropriate in most cases.
Also, while fully implementing IPrincipal and IIdentity is trivial, you could also simply derive from GenericPrincipal and GenericIdentity reducing the amount of code you need to maintain.
In the context of FormsAuthentication you will only have a ticket if the user is authenticated and the User will be an instance of RolePrincipal with an identity of type FormsIdentity and it's implementation of IsAuthenticated is super complex ;-) ...
public bool IsAuthenticated
{
get
{
return true;
}
}
Hope that helps clear things up.
I use a custom UserPrinciple to embed more information about the current user into my pages than the standard GenericPrinciple allows. I didn't find a need to implement my own IIdentity as you can easily leverage the built in FormsIdentity similar to my fashion (I'm not sure if this is divergent from standard practices of Auth for .NET it's worked great in practice for myself though). I did create a custom GuestIdentity that returns a hardcoded IsAuthenticated = false perhaps this could be replaced by just GenericPrinciple I'm not sure off hand if it's abstract or not.
public class UserPrincipal : IPrincipal
{
private readonly IIdentity _identity;
public UserPrincipal()
{
_identity = new GuestIdentity();
var guest = //my custom object
User = guest;
}
public UserPrincipal(HttpContext context)
{
var ident = context.User.Identity as FormsIdentity;
string msg1 = "Context.User.Identity is null for authenticated user.";
if (ident == null) throw new ApplicationException(msg1);
_identity = ident;
string msg2 = "Forms Identity Ticket is null";
if (ident.Ticket == null) throw new AccessViolationException(msg2);
var userData = ident.Ticket.UserData;
...
User = jsonSerializer.Deserialize<User>(userJson);
}
#region IPrincipal Members
public bool IsInRole(string role)
{
return User.Roles.FirstOrDefault(x => x.RoleName == role) != null;
}
public IIdentity Identity
{
get { return _identity; }
}
#endregion
}
Random aside, you can cache data in the Forms Authentication ticket like extended UserData, if you follow this type of idea though make sure you have logic in place that can correctly expire stale data since it's stored on the client computer.
I've noticed that if you try to send an email to an invalid address, an exception is raised:
MailAddress To=new MailAddress("invalidemailaddress","recipientname");
throws:
"The specified string is not in the form required for an e-mail address"
This means that there must be a .Net function which is executed in MailAddress to check if the email address is valid or not. Is there a way to call this 'validate' function directly? This way I won't need to create my own IsValid function.
No but you can make one:
public bool ValidateEmailAddress (string email)
{
if (string.IsNullOrEmpty (email)) return false;
try
{
MailAddress to = new MailAddress (email);
return true;
}
catch (WhateverException e)
{
return false;
}
}
Answering comments. I am aware this technique is regarded as a bad one and with reason. What I would like to point out is that this approach will give you 100% guarantee the .NET mailing library will be able to send to a validated address lately. The problem with Regexes (of which there are plenty) is that each one addresses one particular subset of the set of technically correct addresses as per specification. One would be narrower, the other one would be wider than the subset defined internally in .NET. If you were to use Regex validation, then in the first case your Regex would cut off a portion of the valid addresses (as seen by .NET), in the latter case the validaton will let through addresses that the .NET mailing library won't treat as invalid per its own internal validation. The one true way to make sure you valid set 100% matches the .NET set (or of any other third party library you would use) is to fall for the try/catch approach, unless of course this third party library offers some validation method already.
Yes, there is such a .Net function, but its functionality is unaccessible by "standard" means: MailAdress uses a private ParseAddress method, which in turn uses System.Net.Mime.MailBnfHelper. The latter is an internal class, so it's not (easily) accessible outside the framework itself.
Thus, the only way to use these functions would be to use reflection, which I strongly advise against. Since these functions are undocumented and unaccessible without reflection, their implementation might change and your code might break in future versions of the framework.
There's a good example of an email validation function on CodeProject.
Original Source Code written by Vasudevan Deepak Kumar:
public static bool isEmail(string inputEmail)
{
inputEmail = NulltoString(inputEmail);
string strRegex = #"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}" +
#"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
#".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
Regex re = new Regex(strRegex);
if (re.IsMatch(inputEmail))
return (true);
else
return (false);
}
Unfortunately, there is no way to get at that functionality without reverse-engineering it or using that specific exception, sadly.
The traditional way to validate an email address has always been with regular expressions, but there are lengths you can go beyond that to validate emails even further, if you so wish:
The Forgotten Art of Email Address Validation
You could write your own class:
class EmailAddress
{
private MailAddress _email;
public string Address
{
get
{
return _email == null ? string.Empty : _email.Address;
}
}
public string DisplayName
{
get
{
return _email == null ? string.Empty : _email.DisplayName;
}
}
public string Host
{
get
{
return _email == null ? string.Empty : _email.Host;
}
}
public string User
{
get
{
return _email == null ? string.Empty : _email.User;
}
}
public EmailAddress(string email)
{
try {
_email = new MailAddress(email);
}
catch (Exception) {
_email = null;
}
}
public EmailAddress(string email, string displayName)
{
try {
_email = new MailAddress(email, displayName);
}
catch (Exception) {
_email = null;
}
}
public EmailAddress(string email, string displayName, Encoding displayNameEncoding)
{
try {
_email = new MailAddress(email, displayName, displayNameEncoding);
}
catch (Exception) {
_email = null;
}
}
public bool IsValid()
{
return _email == null ? false : true;
}
public override string ToString()
{
return this.Address;
}
}
Now you use it just as MailAddress but there is now no exception when the Email address is not valid. Instead you call the IsValid method:
var email = new EmailAddress("user#host.com");
if (email.IsValid()) {
...
}
else {
...
}