19Nov, 2023
Reset Your Sitecore Accounts After a Configurable Amount of Time Has Passed
A common question raised in the community is, “How do I unlock a Sitecore account due to bad password attempts?”. The fix is easy enough with a simple SQL command, but why not use a task that will do this for you?
How to Lock Accounts After Failed Password Attempts
The setting to lock accounts will be found in the web.config file, under the membership section. To test, I've set mine to be very short, with maxInvalidPasswordAttempts to be 3, and passwordAttemptWindow is 2. This means the account will lock after 3 failed attempts inside a 2 minute window.
<membership> <providers> <clear /> <add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="core" applicationName="sitecore" maxInvalidPasswordAttempts="3" passwordAttemptWindow="2" /> </providers> </membership>
I intentionally failed a login three times after applying this setting, and checked the Core database with the following query:
SELECT [Email],[IsLockedOut],[LastLockoutDate],[FailedPasswordAttemptCount] FROM [aspnet_Membership] where IsLockedOut = 1
This returns the result:
- Email: user@domain.com
- IsLockedOut: 1
- LastLockoutDate: 2023-11-19 7:00:00 PM
- FailedPasswordAttemptCount: 3
That's exactly what we want. The account is now locked until someone does something about it.
The Quick Fix
Most answers out there will be to just run the following command, which works just fine. The problem is you need a developer to do this every time.
UPDATE [aspnet_Membership] set FailedPasswordAttemptCount=0, IsLockedOut=0 where email='user@domain.com' and IsLockedOut = 1
Ta-Da, your account is ready to be used again! But this can be improved. Let's add a scheduled task to do the work for you.
Automating Account Unlocks
The first thing to do is set up a task. I've already posted about this, but let's just drop a quick and easy config file to run a process once a minute. We'll also add a value for ResetAfterMinutes, which resets locked accounts after the desired time span.
<configuration> <sitecore> <settings> <setting name="SitecoreFundamentals.Tasks.UserAccountLockoutReset.ResetAfterMinutes" value="30" role:require="Standalone or ContentManagement"/> </settings> <scheduling> <agent type="SitecoreFundamentals.Tasks.UserAccountLockoutReset, SitecoreFundamentals" method="Run" interval="00:01:00" role:require="Standalone or ContentManagement"> <donotunlockifcontains hint="list"> <item>sitecore\</item> </donotunlockifcontains> </agent> </scheduling> </sitecore> </configuration>
Now we need a task. It's pretty easy; we'll just iterate over locked accounts and reset those which have been in detention long enough. The list of donotunlockifcontains is optional, and would ignore locked accounts matching their content.
namespace SitecoreFundamentals.Tasks { public class UserAccountLockoutReset { public List<string> DoNotUnlockIfContains { get; private set; } public UserAccountLockoutReset() { this.DoNotUnlockIfContains = new List<string>(); } public bool Run() { var logPrefix = $"[{GetType().FullName}.{MethodBase.GetCurrentMethod().Name}] -> "; int resetAfterMinutes = Sitecore.Configuration.Settings.GetIntSetting("SitecoreFundamentals.Tasks.UserAccountLockoutReset.ResetAfterMinutes", 30); var resetTime = DateTime.UtcNow.AddMinutes(resetAfterMinutes * -1); var lockedUsersNeedingReset = Membership.GetAllUsers().Cast<membershipuser>() .Where(x => x.IsLockedOut && !DoNotUnlockIfContains.Any(p => x.UserName.Contains(p))); try { foreach (var membershipUser in lockedUsersNeedingReset) { if (membershipUser.LastLockoutDate <= resetTime) { Log.Info($"{logPrefix} Unlocking {membershipUser.UserName}", this); membershipUser.UnlockUser(); } else { var timeToUnlock = membershipUser.LastLockoutDate - resetTime; var formattedTimeToUnlock = string.Format("{0:0}.{1:00}", Math.Truncate(timeToUnlock.TotalMinutes), timeToUnlock.Seconds); Log.Info($"{logPrefix} {membershipUser.UserName} will be unlocked in {formattedTimeToUnlock} minutes.", this); } } } catch (Exception ex) { Log.Error($"{logPrefix} {ex}", this); return false; } return true; } } }
That should do it! The User of accounts that are locked for longer than 30 minutes are reset using the UnlockUser() method. You get the following log entries each time this task runs:
14:21:06 INFO Job started: SitecoreFundamentals.Tasks.UserAccountLockoutReset 14:21:06 INFO [SitecoreFundamentals.Tasks.UserAccountLockoutReset.Run] -> extranet\user@domain.com will be unlocked in 28.34 minutes. 14:21:06 INFO Job ended: SitecoreFundamentals.Tasks.UserAccountLockoutReset (units processed: )
And the following will be logged when an account is unlocked:
14:22:06 INFO Job started: SitecoreFundamentals.Tasks.UserAccountLockoutReset 14:22:06 INFO [SitecoreFundamentals.Tasks.UserAccountLockoutReset.Run] -> Unlocking extranet\user@domain.com 14:22:06 INFO Job ended: SitecoreFundamentals.Tasks.UserAccountLockoutReset (units processed: )