when using a stock activedirectorymembership provider
if you try and access a protected page you get you can override the call by implementing a custom provider but just wondering if you knew that bug existed.
[ArgumentException: The parameter 'username' must not be empty.
Parameter name: username]
System.Web.Util.SecUtility.CheckParameter(String& param, Boolean checkForNull, Boolean checkIfEmpty, Boolean checkForCommas, Int32 maxSize, String paramName) +3909143
System.Web.Security.ActiveDirectoryMembershipProvider.CheckUserName(String& username, Int32 maxSize, String paramName) +42
System.Web.Security.ActiveDirectoryMembershipProvider.GetUser(String username, Boolean userIsOnline) +184
System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline) +175
umbraco.requestHandler..ctor(XmlDocument umbracoContent, String url) +3805
umbraco.UmbracoDefault.Page_PreInit(Object sender, EventArgs e) +1294
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +25
System.EventHandler.Invoke(Object sender, EventArgs e) +0
System.Web.UI.Page.PerformPreInit() +49
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1700
Unhandled Execution Error
The parameter 'username' must not be empty.
Parameter name: username
at System.Web.Util.SecUtility.CheckParameter(String& param, Boolean checkForNull, Boolean checkIfEmpty, Boolean checkForCommas, Int32 maxSize, String paramName)
at System.Web.Security.ActiveDirectoryMembershipProvider.CheckUserName(String& username, Int32 maxSize, String paramName)
at System.Web.Security.ActiveDirectoryMembershipProvider.GetUser(String username, Boolean userIsOnline)
at System.Web.Security.Membership.GetUser(String username, Boolean userIsOnline)
at umbraco.requestHandler..ctor(XmlDocument umbracoContent, String url)
at umbraco.UmbracoDefault.Page_PreInit(Object sender, EventArgs e)
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.EventHandler.Invoke(Object sender, EventArgs e)
at System.Web.UI.Page.PerformPreInit()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
I ended up implementing a custom membership provider and as well as this i used a custom role provider to cache my roles so the AD hit didn't take so long when calling stuff like IsProtected. The reason being every time you check if a node is protected umbraco tries to get all roles!
public class CustomMembershipProvider : ActiveDirectoryMembershipProvider
{
public override MembershipUser GetUser(string username, bool userIsOnline)
{
if (!string.IsNullOrWhiteSpace(username))
{
var result = (MembershipUser)HttpContext.Current.Session[username];
if (result == null || (string)HttpContext.Current.Session["recentUser"] != username)
{
result = base.GetUser(username, userIsOnline);
HttpContext.Current.Session[username] = result;
HttpContext.Current.Session["recentUser"] = username;
}
return result;
}
else
{
return null;
}
}
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
return base.CreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
return base.FindUsersByEmail(emailToMatch, pageIndex, pageSize, out totalRecords);
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
return base.FindUsersByName(usernameToMatch, pageIndex, pageSize, out totalRecords);
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
return base.GetAllUsers(pageIndex, pageSize, out totalRecords);
}
public override int GetNumberOfUsersOnline()
{
return base.GetNumberOfUsersOnline();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
return base.GetUser(providerUserKey, userIsOnline);
}
public override string GetUserNameByEmail(string email)
{
return base.GetUserNameByEmail(email);
}
public override bool ValidateUser(string username, string password)
{
return base.ValidateUser(username, password);
}
}
The root cause of this problem is that ActiveDirectoryMembershipProvider throws an ArgumentException when username is empty. This occurs in umbraco.requesthandler.requestHandler() where it calls Membership.GetUser() and there is no currently logged in user.
I was concerned about storing the user object in a session state variable and possible side effects. The code I have checks if the username is null/whitespace/empty and returns null as Umbraco expects. It also catches ArgumentException and returns null for Umbraco. Otherwise, it calls ActiveDirectoryMembershipProvider.GetUser() without any caching.
public override MembershipUser GetUser(string username, bool userIsOnline) { // If username is empty, whitespace or null, return null. Will fail at base.GetUser() and avoids exception overhead. if (String.IsNullOrWhiteSpace(username)) { return null; }
// If username is provided, try login. try { return base.GetUser(username, userIsOnline); } catch (ArgumentException) { // If ArgumentException is thrown, no user was logged in or username was incorrectly formatted. Return null to conform to what Umbraco expects. return null; } }
Active Directory BUG in 4.7
Hi guys,
when using a stock activedirectorymembership provider
if you try and access a protected page you get you can override the call by implementing a custom provider but just wondering if you knew that bug existed.
I'm encountering the same problem with v4.7.1. Can you explain more about what you did to work around this problem?
Hi Brian,
I ended up implementing a custom membership provider and as well as this i used a custom role provider to cache my roles so the AD hit didn't take so long when calling stuff like IsProtected. The reason being every time you check if a node is protected umbraco tries to get all roles!
this was my web.config file:
<membership defaultProvider="MyADMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear />
<add name="MyADMembershipProvider" type="Project.Web.CustomMembershipProvider" connectionStringName="ADConnectionString" connectionUsername="someusername" connectionPassword="somepassword" connectionProtection="None" attributeMapUsername="SAMAccountName" enableSearchMethods="true" />
<add name="MyADMembershipProvider" type="Project.Web.CustomMembershipProvider" connectionStringName="ADConnectionString" connectionUsername="someusername" connectionPassword="somepassword" connectionProtection="None" attributeMapUsername="SAMAccountName" enableSearchMethods="true" />
<add name="UmbracoMembershipProvider" type="umbraco.providers.members.UmbracoMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Another Type" passwordFormat="Hashed" />
<add name="UsersMembershipProvider" type="umbraco.providers.UsersMembershipProvider" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" passwordFormat="Hashed" />
</providers>
</membership>
<!-- added by NH to support membership providers in access layer -->
<roleManager enabled="true" defaultProvider="Custom_ActiveDirectoryRoleProvider">
<providers>
<clear />
<add name="Custom_ActiveDirectoryRoleProvider" type="Project.Web.CustomRoleProvider" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
<add name="UmbracoRoleProvider" type="umbraco.providers.members.UmbracoRoleProvider" />
</providers>
</roleManager>
The root cause of this problem is that ActiveDirectoryMembershipProvider throws an ArgumentException when username is empty. This occurs in umbraco.requesthandler.requestHandler() where it calls Membership.GetUser() and there is no currently logged in user.
I was concerned about storing the user object in a session state variable and possible side effects. The code I have checks if the username is null/whitespace/empty and returns null as Umbraco expects. It also catches ArgumentException and returns null for Umbraco. Otherwise, it calls ActiveDirectoryMembershipProvider.GetUser() without any caching.
is working on a reply...