Little problem
December 11th, 2006 by Dario Solera | Filed under Development.Last week I was implementing the “data moving” feature, which lets you move the data from a Provider to another. You can already move Pages and their associated data (Categories, Snippets, etc.); all seems to work fine.
When I started writing the code to do the same thing for Users I though “It will be trivial compared to Pages [that required a hundred LOC], there is only a list of users to move”. Then, I realized that Users cannot be moved. I don’t know how couldn’t I have figured that out before.
Actually, Users are stored in their nice Users.cs file, containing one line for each account:
Username|Password_MDH_Hash|Email|Status|Date_Time|Auth_Level
Don’t you see the problem? Take a closer look. Nothing? I’d better report the signature of method that is responsible of the user creation in the IUsersStorageProvider:
UserInfo AddUser(string username, string password, string email,
bool active, bool admin)
Do you see it now? The method needs the clear-text password, but only the MD5 Hash of the password is available (stored). Therefore Users cannot be moved. Unless the IUsersStorageProvider is forced to use MD5 Hash values instead of passwords, which is definitely a big no-no in this kind of architecture (it would prevent from interacting with any preexistent system, and anyway stuck the data to a predefined, unchangeable format forever).
So, the only thing we can do is to use a hack in our SQL Server and MySQL Providers: they could use the same MD5 Hash for storing passwords, and if the aforementioned method is invoked with a special tag in the password parameter (say ##MD5PWD##), then it would be treated directly as a Hash instead of a clear-text password. Obviously, the code that actually moves the data should implement some dedicated logic to discern the two cases. This would mean that you could move Users only to and from “our” providers, but in general you could not move them.
I’m sure this is not a nice way to implement things, so I guess we won’t implement it. We should have thought about this before (about six months ago), but now it’s late. What do you think?



I think it’s an unnecessary feature. Do not care about it. (If anyone wants to move his users, he always can do it by some external (storage dependent) code, like SQL Server import utility.)
Can I beg one small change in Login process? Could you do not search user in the AllUsers collection, before perform IUsersStorageProvider.TestAccount? Please.
Login process: may I ask why? It’s faster the way it is. Anyway, it cannot be done without modifying the global “behavior” of the Users Providers.
I have too many users in the Active Directory. And I cannot enumerate all of them.
Hi Dario,
you could implement the feature in a really simple but dirty way. Say that users are moved but are not activated. You move the user to the new environment and send an email to reactivate the account because of a technical change of the wiki system. In this case every user has to reactivate his account and save a new password. I think this should not be a really big problem.
Just an idea, maybe you think about it.
Greets Ben.
@Djuffin
I thought about it, and it’s definitely not possible. It would require to modify the interface IUsersStorageProvider and the global functioning behavior of the Users.
If you really cannot enumerate all the users in Active Directory, you could simply add some code in the Login page that, if not found, asks for the user directly to each provider using the TestAccount method. This way, though, you cannot retrieve the account object and therefore you don’t know whether the user is an administrator. Maybe this is not a problem and you can always set to false the “Admin” session variable.
Tell me if you need help (via email or in the forum).
Dario, thank you for your advice. I really modified Login page for these purposes.
Why not just use the ASP.NET authentication provider? That provider not only supports users in SQL server, but also in Active Directory. It also allows me to override it with my own custom provider for all ASP.NET 2.0 applications.