I have a base abstract class that needs an algorithm for Authentication. I have two implementations of this, one will hash a password and compare it to a stored hash, the other will use windows active directory.
But before actually doing the hash check or the windows authentication, I have extra workflow logic that must absolutely be implemented. So things like failedPasswordCount, lastAuthenticationAttemptDate, IsUserApproved, etc. must always be modified or used in the same way no matter the algorithm for Authentication.
This problem seems to me like it can be resolved using Template Method Pattern, except that, the information required to implement my two Authentication methods are different depending on which one is used.
public abstract class User
{
public bool Authenticate() // my template method
{
lastAuthenticationAttemptDate = DateTime.UtcNow();
if(IsUserApproved)
{
if(DoAuthenticate()) // implemented by childs
{
return true;
}
else
{
failedPasswordCount++;
return false;
}
}
}
public abstract DoAuthenticate();
}
public UserWindowsLogon : User
{
public override bool DoAuthenticate(string windowsDomain, string password)
{
...
}
}
public UserApplicationLogon : User
{
public override bool DoAuthenticate(string password)
{
...
}
}
What is the best way to solve this problem? Is there another known pattern that already addresses this? Or anyone has a good idea?
Assuming your client code knows what to do (what actual parameters should be applied), you can easily introduce a class hierarchy around your authentication, thus making it possible to declare the contract on the base class of such hierarchy.
public abstract DoAuthenticate( AuthenticationContext context );
...
public UserWindowsLogon : User
{
public override bool DoAuthenticate( AuthenticationContext context )
{
if ( context is UserWindowsAuthenticationContext )
{
// proceed
}
}
}
public UserApplicationLogon : User
{
public override bool DoAuthenticate( AuthenticationContext context )
{
if ( context is UserAplicationAuthenticationContext )
{
// proceed
}
}
}
public abstract class AuthenticationContext { }
public class UserWindowsAuthenticationContext : AuthenticationContext
{
public string windowsDomain;
public string password;
}
public class UserApplicationAuthenticationContext : AuthenticationContext
{
public string password;
}
You could keep your base interface's "DoAuthenticate()" clean of parameters this way
public UserWindowsLogon : User
{
public string windowsDomain;
public string password;
public override bool DoAuthenticate()
{
// Check and use windowsDomain/password values here
}
}
or
public UserApplicationLogon : User
{
public UserApplicationLogon(string password) : base()
{
this.password = password;
}
private string password;
public override bool DoAuthenticate()
{
// use password value here
}
}
And provide the parameter values to use when you instantiate the User derived object.
This is really an example of a Strategy pattern (where is strategy is the authentication/validation mechanism) meets a Composite pattern
Validation is frequently a Composite pattern. When you break it down, you want to separate the what you want to from the how you want to do it, you get:
If foo is valid
then do something.
Here we have the abstraction is valid
I just wrote about, essentially, your case here. If you find this answer to be most awesesome, then give IT a check/vote too :)