For an Umbraco 4.7.0 instance we are trying to extend the existing UsersMembershipProvider so that it will track failed login attempts and when a limit is exceeded it will lock that user for a certain number of minutes.
Now with the available documentation I'm already aware that I should extend the ValidateUser of the UsersMembershipProvider. I also already know how to validate the user.
What I don't know are the following to points:
1) How do I track the failed login attempts and lock the user for a certain time frame?
2) How do I configure this in the web.config of Umbraco? (the documentation is a bit unclear on the how/why)
I haven't tried it on my own, but the implementation of umbraco membership provider says that
"To enable lock/unlocking, you need to add a 'bool' property on your membertype and add the alias of the property in the 'umbracoLockPropertyTypeAlias' attribute of the membership element in the web.config."
I'm talking about the UsersMembershipProvider NOT the UmbracoMembershipProvider. So what I'm extending is the backend login, not website members. The latter does indeed support this, but the UsersMembershipProvider does not have this implemented.
Basically, like Rodion writes, you can access the "Microsoft" Membership Provider properties by adding the corresponding propoerties in your member type.
So in your case you need to create two properties on your property type:
a property of type numeric, to retain the failed login attempts, e.g. "failed_logins" a property of type true/false, to retain the lock status, e.g. "lock"
You can see the properties displayed on the details of yout members in the backoffice
Then, in your web.config file, you need to add the following attributes to yout membership provider definition
From then on, if a user fails to login, the failed_logins property will be incremented. If you exceed the maxInvalidPasswordAttempts value, the the member gets locked automatically.
Since you are working in .Net 4.0, there is also a property passwordAttemptWindow that you can use and that might help you in "locking the user for a certain time" (more info at http://msdn.microsoft.com/en-us/library/system.web.security.membership.passwordattemptwindow.aspx). Otherwize you might need to catch the failed login attempts and do the "temporary locking" in some custom method.
Well yes that's my intention to add this functionality to UsersMembershipProvide, like I mentioned in my opening post.
However, I do not know enough about the workings of these memberschip providers, their implementation and life cycle to know how to do this and store the needed information (and act accordingly on this stored information).
I do not think it is very complicated, but I have never written a custom membership provider so I cannot help you a lot about this :-S
I guess you basically need to inherit from the UsersMembershipProvider and then override the methods you need. In your case the ValidateUser, like you mention.
As far as storing the data is concerned, the most obvious option is to store it in the database. Now, if you don't have too many backend users, and if you do not need to actually display/manually modify the data, you might consider storing it in memory in the site Application object, like an array of simple objects (user name - failed attempts - locked until) or something like that. You should be able to access this from your ValidateIUser method, and that way you get rid of database concerns.
As far as the web config is concerned, if you want the "locking time" to be configurable, I don't know how to put it in the membership provider definition. But you can always store it in the appSettings part if you need.
VoilĂ , maybe somebody else from the forum with experience in custom providers will be able to give you better hints :-)
If I set those it doesn't work. But I've seen people use a SqlMembershipProvider, no idea though how those two exactly interact and what I need to configure.
Not sure, but I think you just need to configure the database to use the SqlMembershipProvider (this is done via the aspnet_regsql command), and then change the UsersMembershipProvider entry in your web.config file by the following:
When I try to set up umbraco like described above I get the following error:
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: The connection name 'umbracoDbDSN' was not found in the applications configuration or the connection string is empty.
Now the umbracoDbDSN is defined, but in appsettings in my configuration file (this is what umbraco uses to connect to the database). But the error in itself isn't so strange as the connectionStrings element contains a <remove name="LocalSqlServer" /> element.
But I don't know if I can enable this again as I have no idea if this should be off, and if it is turned on what the consequences would be.
I will check this tonight at home as I used SqlMembership for a previous project, but my guess is that it can't be any harm if you "duplicate" your UmbracoDbDSN entry to the "connections" part of your web. Especially since it will contain the same value as the one in the appSettings part.
This is due to the fact that the Microsoft SQL Membership provider looks for its connection strings in the connections part, and not to the custom appSettings entry.
I think you can leave the "remove" entry as it is, just add the new UmbracoDbDSN entry.
I think the problem is that the current users where created using the UsersMembershipProvider, and therefore are not stored in the tables used by the SqlMemebershipProvider... You can check this by looking at the tables aspnet_users and aspnet_members in your database, they will most probably be empty, which is why you cannot authenticate.
So, either you need to re-setup your site, but directly with the SqlMembershipProvide, which means that at install time, your initial admin user will be created in the correct table: aspnet_users, either you can try to copy the users from the Umbraco users table to the aspnet_users table... But there you might get problems if the paswword encryption are not the same in both systems.
Anyway, if re-setup of the site is not a direct option, what I would first do is the following: manually create an entry in aspnet_users and aspne_members (one is linked to the other, I don't remember in which direction), with a clear password. Set the PasswordFormat to "Clear" in your web.config, and try to login with that user. If it works, at least you'll know that the "only" issue is to migrate the users to the correct tables...
The problem was indeed as Michael described: "I think the problem is that the current users where created using the UsersMembershipProvider, and therefore are not stored in the tables used by the SqlMemebershipProvider... You can check this by looking at the tables aspnet_users and aspnet_members in your database, they will most probably be empty, which is why you cannot authenticate."
If you follow the instructions in this post, and then add users to the mentioned tables, everything Michael has said should work.
Extending the UsersMembershipProvider
For an Umbraco 4.7.0 instance we are trying to extend the existing UsersMembershipProvider so that it will track failed login attempts and when a limit is exceeded it will lock that user for a certain number of minutes.
Now with the available documentation I'm already aware that I should extend the ValidateUser of the UsersMembershipProvider. I also already know how to validate the user.
What I don't know are the following to points:
1) How do I track the failed login attempts and lock the user for a certain time frame?
2) How do I configure this in the web.config of Umbraco? (the documentation is a bit unclear on the how/why)
I haven't tried it on my own, but the implementation of umbraco membership provider says that
"To enable lock/unlocking, you need to add a 'bool' property on your membertype and add the alias of the property in the 'umbracoLockPropertyTypeAlias' attribute of the membership element in the web.config."
I'm talking about the UsersMembershipProvider NOT the UmbracoMembershipProvider. So what I'm extending is the backend login, not website members. The latter does indeed support this, but the UsersMembershipProvider does not have this implemented.
Hi Collin,
Did you have a look at the following resources?
http://www.mortenbock.dk/blog/2009/04/01/setting-up-membership-in-umbraco.aspx
our.umbraco.org/.../umbracomembershipprovider-properties
Basically, like Rodion writes, you can access the "Microsoft" Membership Provider properties by adding the corresponding propoerties in your member type.
So in your case you need to create two properties on your property type:
a property of type numeric, to retain the failed login attempts, e.g. "failed_logins"
a property of type true/false, to retain the lock status, e.g. "lock"
You can see the properties displayed on the details of yout members in the backoffice
Then, in your web.config file, you need to add the following attributes to yout membership provider definition
From then on, if a user fails to login, the failed_logins property will be incremented. If you exceed the maxInvalidPasswordAttempts value, the the member gets locked automatically.
Since you are working in .Net 4.0, there is also a property passwordAttemptWindow that you can use and that might help you in "locking the user for a certain time" (more info at http://msdn.microsoft.com/en-us/library/system.web.security.membership.passwordattemptwindow.aspx). Otherwize you might need to catch the failed login attempts and do the "temporary locking" in some custom method.
Hope this helps.
Cheers,
Michael.
Backend users, not frontend members....
Woops, sorry :-S
Took too long to write my post, did not see your answer...
Unfortunately I don't know that much about the Backend users stuff...
Maybe you could build your own provider that extends the UserMembershipProvider and adds the extra properties needed?
Cheers,
Michael.
Well yes that's my intention to add this functionality to UsersMembershipProvide, like I mentioned in my opening post.
However, I do not know enough about the workings of these memberschip providers, their implementation and life cycle to know how to do this and store the needed information (and act accordingly on this stored information).
Hi Collin,
I do not think it is very complicated, but I have never written a custom membership provider so I cannot help you a lot about this :-S
I guess you basically need to inherit from the UsersMembershipProvider and then override the methods you need. In your case the ValidateUser, like you mention.
As far as storing the data is concerned, the most obvious option is to store it in the database. Now, if you don't have too many backend users, and if you do not need to actually display/manually modify the data, you might consider storing it in memory in the site Application object, like an array of simple objects (user name - failed attempts - locked until) or something like that. You should be able to access this from your ValidateIUser method, and that way you get rid of database concerns.
As far as the web config is concerned, if you want the "locking time" to be configurable, I don't know how to put it in the membership provider definition. But you can always store it in the appSettings part if you need.
VoilĂ , maybe somebody else from the forum with experience in custom providers will be able to give you better hints :-)
Cheers,
Michael.
Well I've seen hints in the documentation that you can user a profile for that. But no clue if and how i can use this for the UsersMembershipProvide.
Neither do I :-S
Did you try just setting the memebrship provider properties of the UsersMembershipProvider? I found this post http://our.umbraco.org/forum/using/ui-questions/21302-Configuring-password-complexity-for-back-office-users where they seem to indicate that it's just about configuring the web.config... Maybe worth a try if you didn't already...
Cheers,
Michael.
If I set those it doesn't work. But I've seen people use a SqlMembershipProvider, no idea though how those two exactly interact and what I need to configure.
Not sure, but I think you just need to configure the database to use the SqlMembershipProvider (this is done via the aspnet_regsql command), and then change the UsersMembershipProvider entry in your web.config file by the following:
Cheers,
Michael.
When I try to set up umbraco like described above I get the following error:
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: The connection name 'umbracoDbDSN' was not found in the applications configuration or the connection string is empty.
Now the umbracoDbDSN is defined, but in appsettings in my configuration file (this is what umbraco uses to connect to the database). But the error in itself isn't so strange as the connectionStrings element contains a <remove name="LocalSqlServer" /> element.
But I don't know if I can enable this again as I have no idea if this should be off, and if it is turned on what the consequences would be.
Hi Colin,
I will check this tonight at home as I used SqlMembership for a previous project, but my guess is that it can't be any harm if you "duplicate" your UmbracoDbDSN entry to the "connections" part of your web. Especially since it will contain the same value as the one in the appSettings part.
This is due to the fact that the Microsoft SQL Membership provider looks for its connection strings in the connections part, and not to the custom appSettings entry.
I think you can leave the "remove" entry as it is, just add the new UmbracoDbDSN entry.
Cheers,
Michael.
I suspected it wouldn't do any harm. But I've learned not to act on suspicions when configuring the web.config (very bad things can happen).
So I did add the new connection string as follows (please note I obfuscated the values):
<connectionStrings>
<remove name="LocalSqlServer" />
<add name="umbracoDbDSN" connectionString="server=server;database=database;user id=user;password=password" providerName="System.Data.SqlClient" />
</connectionStrings>
And the membership provider is now configured as follows:
<membership defaultProvider="UmbracoMembershipProvider" userIsOnlineTimeWindow="60">
<providers>
<clear />
<add name="UmbracoMembershipProvider" type="umbraco.providers.members.UmbracoMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Company" passwordFormat="Hashed" />
<add name="UsersMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="umbracoDbDSN" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10"/>
</providers>
</membership>
But after these changes I'm no longer able to log in. Which I can again after I switch back to the default umbraco "UsersMembershipProvider".
So I'll wait for you to check how it worked in your previous project. :)
OK I wiil check then ;-)
What do you get as error? It could be that the connectionstring needs to be in a different format.
I keep you posted.
Cheers,
Michael.
The umbraco interface just gives the custom invalid credentials animation, the event log on the server says the following (obfuscated):
Event code: 4006
Event message: Membership credential verification failed.
Event time: 11/28/2011 12:05:31 PM
Event time (UTC): 11/28/2011 11:05:31 AM
Event ID: 2c7bd6285cd746888617529e5143a902
Event sequence: 36
Event occurrence: 5
Event detail code: 0
Application information:
Application domain: ************************
Trust level: Full
Application Virtual Path: /
Application Path: ***************************
Machine name: *********************
Process information:
Process ID: 19504
Process name: w3wp.exe
Account name: NT AUTHORITY\NETWORK SERVICE
Request information:
Request URL: *********************
Request path: /umbraco/login.aspx
User host address: ******************
User:
Is authenticated: False
Authentication Type:
Thread account name: NT AUTHORITY\NETWORK SERVICE
Name to authenticate: **************
Custom event details:
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
Hi Collin,
I think the problem is that the current users where created using the UsersMembershipProvider, and therefore are not stored in the tables used by the SqlMemebershipProvider... You can check this by looking at the tables aspnet_users and aspnet_members in your database, they will most probably be empty, which is why you cannot authenticate.
So, either you need to re-setup your site, but directly with the SqlMembershipProvide, which means that at install time, your initial admin user will be created in the correct table: aspnet_users, either you can try to copy the users from the Umbraco users table to the aspnet_users table... But there you might get problems if the paswword encryption are not the same in both systems.
Anyway, if re-setup of the site is not a direct option, what I would first do is the following: manually create an entry in aspnet_users and aspne_members (one is linked to the other, I don't remember in which direction), with a clear password. Set the PasswordFormat to "Clear" in your web.config, and try to login with that user. If it works, at least you'll know that the "only" issue is to migrate the users to the correct tables...
Cheers,
Michael.
Hey colin, i'm looking to do something similar. did you have any progress with this?
Currently this hasn't been implemented.
The problem was indeed as Michael described: "I think the problem is that the current users where created using the UsersMembershipProvider, and therefore are not stored in the tables used by the SqlMemebershipProvider... You can check this by looking at the tables aspnet_users and aspnet_members in your database, they will most probably be empty, which is why you cannot authenticate."
If you follow the instructions in this post, and then add users to the mentioned tables, everything Michael has said should work.
thanks for that colin. how did you get around using GUID for id?
I'm not sure what you mean with getting "around using the GUID for id"?
is working on a reply...