Does the default Membership Provider's OnValidatingPassword have to be overridden in custom implementations?
Asked Answered
L

1

7

I am working to implement a custom Membership Provider for my .net application. I have set up the configuration for a minimum number of characters and non-alphanumeric characters, but it seems to let passwords through anyway, even when they break the rules.

OnValidatingPassword is a virtual method. The example from Microsoft does not override the method.

This question grapples with the same problem, but the author gave up on getting the answer to his question and simply overrode the function. This answer states that one does not have to override the function to have it work.

Does the base function not do anything? When I override OnValidatePassword, and simply call the base class, my function gets hit, but it never rejects my too-simple passwords.

Code sample (with a custom CreateUser function)

protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
        {                        
             base.OnValidatingPassword(e);
        }
        //
        // MembershipProvider.CreateUser
        //
        public MembershipUser CreateUser(string username, string password, string globalIdentifier, string firstName, string lastName, 
            string birthDate, object providerUserKey, out MembershipCreateStatus status)
        {
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, password, true);
            OnValidatingPassword(args);

            if (args.Cancel)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }
Lovesome answered 4/10, 2011 at 21:5 Comment(0)
P
6

The documentation for MembershipProvider.OnValidatingPassword only states that that it raises the ValidatingPassword event if a handler is registered, not that it actually validates the password.

Looking at the method in Reflector confirms this:

protected virtual void OnValidatingPassword(ValidatePasswordEventArgs e)
{
    if (this._EventHandler != null)
    {
        this._EventHandler(this, e);
    }
}

It is confusing, but I believe the intent is that this provides a hook for external logic to participate in password validation; A custom provider would still need to write its own validation logic.

If you take a look at the source code for the SQL Membership Provider (download the Provider Toolkit Samples), you'll see that it includes logic to validate the password, and also calls OnValidatingPassword. The following code is from the CreateUser method:

if( password.Length < MinRequiredPasswordLength )
{
    status = MembershipCreateStatus.InvalidPassword;
    return null;
}

int count = 0;

for( int i = 0; i < password.Length; i++ )
{
    if( !char.IsLetterOrDigit( password, i ) )
    {
        count++;
    }
}

if( count < MinRequiredNonAlphanumericCharacters )
{
    status = MembershipCreateStatus.InvalidPassword;
    return null;
}

if( PasswordStrengthRegularExpression.Length > 0 )
{
    if( !Regex.IsMatch( password, PasswordStrengthRegularExpression ) )
    {
        status = MembershipCreateStatus.InvalidPassword;
        return null;
    }
}

ValidatePasswordEventArgs e = new ValidatePasswordEventArgs( username, password, true );
OnValidatingPassword( e );

if( e.Cancel )
{
    status = MembershipCreateStatus.InvalidPassword;
    return null;
}

Edit

I think part of the confusion is based on the name of OnValidatingPassword, and that it seems to imply that it is handling password validation, rather than raising an event to let other code validate the password. For what it's worth, I understand the confusion - it would probably be clearer if the method had been named RaiseValidatingPasswordEvent.

In any case, you can check the Event Design guidelines for .NET 4. About halfway down the page, you'll find this:

Do use a protected virtual method to raise each event.

The name of the protected virtual method should be the same as the event name prefixed with On. For example, the protected virtual method for an event named "TimeChanged" is named "OnTimeChanged".

Poppyhead answered 4/10, 2011 at 21:37 Comment(9)
I may not have made it clear, but when I do override the base method, my method does get called. So it's not a question of whether or not the method gets called -- it just seems to not do anythingLovesome
Right - it doesn't do anything besides raise the ValidatingPassword event. OnValidatingPassword does not do any validation itself.Poppyhead
Thanks for the response. How can OnValidatingEvent be responsible for raising the ValidatingPassword event -- isn't it responding to the ValidatingPassword event?Lovesome
It's common to create virtual methods to raise events named OnSomeEvent(EventArgs args) and then actually raise the event there. See this answer. So OnValidatingEvent isn't responding to the event - it's raising it.Poppyhead
@pc1oad1etter, please see my edit - I added a better source of information regarding the name of OnValidatingPassword. HTH.Poppyhead
Thanks for your time. Let me see if I can summarize your position -- the base class does do something (it raises the event.) However, the implementer is required to handle even cases such as minimum password length on his own. Additionally, the Microsoft example is either incomplete or in error, as is the answer I linked to that says one does not need to override the method. Accurate?Lovesome
@Lovesome yes, that is accurate. I believe that example is incorrect - nowhere do they have code that actually validates the password, and we know that MembershipProvider does not do it. The answer you linked to is correct - you can override OnValidatingPassword and put your validation logic there, but you don't have to - see my next comment.Poppyhead
Other ways you could do this instead of overriding OnValidatingPassword: (1) attach a handler to the ValidatingPassword event with the validation logic and let the base MembershipProvider.OnValidatingPassword call it, (2) put the validation logic in the CreateUser method (as the SqlMembershipProvider does), or (3) put validation logic in a separate method in your provider and call that method from CreateUser, ChangePassword, and wherever else you might need to validate a password.Poppyhead
I'm marking this as correct, but I'm pretty surprised to hear that the linked resources have such misinformation.Lovesome

© 2022 - 2024 — McMap. All rights reserved.