SLK 1.3.1RC1 - GetAllRoles exception patch/workaround

Aug 28, 2008 at 6:25 AM

Hi Jay,

We got the RC1 version installed on our production machine, where the 0x80040E14 error was being thrown before. I can see by looking at the source code that the patch is in there, but it doesn't seem to be trapping the error. I still get this in the event log:-

SharePoint Learning Kit Error

Microsoft.SharePoint.SPException: Exception from HRESULT: 0x80040E14 ---> System.Runtime.InteropServices.COMException (0x80040E14): Exception from HRESULT: 0x80040E14

at Microsoft.SharePoint.Library.SPRequestInternalClass.GetAllRolesForCurrentUser(String bstrUrl, Guid guidScopeId, UInt32& pdwRowCount, Object& pvarRoleData)

at Microsoft.SharePoint.Library.SPRequest.GetAllRolesForCurrentUser(String bstrUrl, Guid guidScopeId, UInt32& pdwRowCount, Object& pvarRoleData)

--- End of inner exception stack trace ---

at Microsoft.SharePoint.Library.SPRequest.GetAllRolesForCurrentUser(String bstrUrl, Guid guidScopeId, UInt32& pdwRowCount, Object& pvarRoleData)

at Microsoft.SharePoint.SPSecurableObjectImpl.get_AllRolesForCurrentUser()

at Microsoft.SharePoint.SPWeb.get_AllRolesForCurrentUser()

at Microsoft.SharePointLearningKit.SlkStore.IsInRole(SPWeb spWeb, String roleDefinitionName)

at Microsoft.SharePointLearningKit.SlkStore.EnsureInstructor(SPWeb spWeb)

at Microsoft.SharePointLearningKit.SlkStore.GetNewAssignmentDefaultProperties(SPWeb destinationSPWeb, String location, Nullable`1 organizationIndex, SlkRole slkRole, LearningStoreXml& packageWarnings)

at Microsoft.SharePointLearningKit.ApplicationPages.AssignmentPropertiesPage.OnPreRender(EventArgs e)

Coordinator
Aug 28, 2008 at 6:21 PM
I believe I see the error.  I've uploaded a patched set of binaries to:

http://www.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=SLK&DownloadId=42448

Can you test that out and see whether this works?
Aug 28, 2008 at 10:56 PM
Will do - thanks for the prompt response. It might take a day or so though - this is our production machine and we need to schedule anything that requires an iisreset. Will let you know as soon as I can.

Thanks.

Ron
Aug 31, 2008 at 9:47 AM

 

Well the error is successfully trapped this time, but I'm afraid it fails to then correctly figure out that the user (me) really is a member of the "SLK Instructor" group. When I try to assign some learning material, I get an error message saying "could not assign material as you are not an istructor on this site" (or words to that effect).

Let me know if there's anything I can do to help diagnose this.

 

Ron

Sep 2, 2008 at 7:43 AM
Edited Sep 2, 2008 at 7:55 AM

I wrote a little "spike" program to try to figure out why the patch doesn't seem to be working, but it's left me more confused than before.

First of all I used the "IsInRole" code snippet from the SlkApi.cs file in the 1.3.1RC1 release. As mentioned above, this somehow doesn't trap the error, but Jay's (binary) update did. I managed to get my version of it to trap the error by changing the appropriate lines of code to read as follows:

try{

//In some cases this fails and we must take an alternate approach.

roleDefinitionCollection = spWeb.AllRolesForCurrentUser;

}

catch(Exception ex) {

if (!ex.Message.EndsWith("0x80040E14")) // Not the specific case we're looking for, rethrow

throw;
...

In other words, I took out the bit that was looking for a certain type of exception and replaced it with a normal System.Exception and changed the test to just check the end of the exception message.

Anyway, this trapped the error and my test app called this routine thus:-

foreach

(SPWeb myWeb in checkedWebs)  {

if (!myWeb.EffectivePresenceEnabled) {MessageBox.Show("Effective presence NOT implemented on this site"); }

if (IsInRole(myWeb, "SLK Instructor")) { 

MessageBox.Show("Yes, you seem to be an instructor");

}

 

else {

MessageBox.Show("No, you do not appear to be an instructotr");

}

}

And that returned the message "Yes, you seem to be an instructor".

So - that works, but I still get the error when I try to allocate training material to users (with the error message in my last post above). So - could the error be elsewhere now? Any ideas?

 

 

Coordinator
Sep 2, 2008 at 6:28 PM
Edited Sep 2, 2008 at 6:29 PM
I've checked in my fix to the patch so you have the source code for the binaries I uploaded.

Can you attach a debugger and step through the problem when it occurs?  If you don't know how to do this, can you send me a test program that I can use to reproduce the issue?  Is it just a matter of overdriving Active Directory?

Wiki page on attaching a debugger: http://www.codeplex.com/SLK/Wiki/View.aspx?title=attaching%20a%20debugger&referringTitle=Checking%20In

 - jcb
Sep 3, 2008 at 3:05 AM

Hi Jay,

Thanks for the source code - I was able to verify using my test application that the modification you made to the COMException worked as intended, at least in my little bit of test code,which means that the error really is elsewhere.

Do you know if I am able to use the remote debugger from VS2005 for this? I don't have 2008 so am unable to actually compile the project. I'm assuming that the symbols are in the .pdb files? The only .pdb files in the binary for RC1 are under Services\bin - I'm assuming that I need a SharePointLearningKit.pdb file to go along with the Microsoft.SharePointLearningKit.dll that is installed in the GAC?

I'm not sure what you mean by the problem being one of overdriving Active Directory. From what we've been able to find out about this bug that we're trying to work around, it has something to do with the SharePoint server and the SQLServer database somehow getting out of sync. One suggested solution on the net is to force an upgrade of the SQLServer database. Our IT dept have scheduled this for the coming weekend - if that fixes it then I won't be able to reproduce the problem anymore (the problem doesn't exist at all on our Sandbox machine) - that gives us about 3 days to figure it out! (maybe - maybe not if the upgrade doesn't fix anything).

That also makes it tricky to make a test program that reproduces the issue - the problem is with SharePoint. I'll try to do what I can and let you know how I get on.

Ron
Coordinator
Sep 3, 2008 at 11:03 PM
I don't have any experience in using a VS 2005 debugger with VS 2008 binaries.  You should be able to use Visual C# Express 2008, though there is no remote debugger with that so you'll have to debug locally on the server.
Sep 8, 2008 at 4:37 AM

Well the good news/bad news is that our forced upgrade of the database made no difference - so we still have a SharePoint server throwing 0x80040E14 errors, so we are able to reproduce and hopefully diagnose the problem.

Since my last post I have installed VS2008 90 day trial to be able to compile the Learning kit, and have successfully managed to use VS2005 to debug it. I'm still confused, but a little less so.

Whether "IsInRole" is called from within my test app or from a SharePoint thread, the trapping of the exception is the same. However, the behaviour of the mitigating code is slightly different, and I think it might have to to with the fact that I am a site collection administrator. If I log in as another test user who has the same group memberships on the site, that user is able to allocate learning material whereas I, as myself, am not.

In this section of the code:-

SPGroup

group = roleAssignment.Member as SPGroup;

 

 

if (group != null)

 

{

 

if (!group.ContainsCurrentUser)

 

{

 

continue; // the roleAssignment is for a group the user is not a member of

 

}

}

the group "CM Trainers" contains my user - I can see myself if I expand the "Users" property in the debugger. However, the "group.ContainsCurrentUser" returns false, so it seems to be confused about who the "CurrentUser" is. I can only assume that the "RunWithElevatedPrivileges" part is behaving differently for the site collection administrator than it does for an ordinary user.

I will try to verify this and report back.

Ron

 

Sep 8, 2008 at 6:16 AM

OK - it's not that "RunWithElevatedPrivileges" is behaving differently for me and for the test user - it is behaving consistently. The test user isn't throwing the 0x80040E14 error at all.

Under "RunWithElevatedPrivileges" the current user is "System Account" (SHAREPOINT\System), whic explains why group.ContainsCurrentUser is always returning false.

So is it possible to move the bulk of this outside the "RunWith ElevatedPrivileges" block? Is it ok to define the SPRoleAssignmentCollection outside this block and just use the elevated privileges block to get that collection? So that the test involving the current user is done afterwards?

I'll try this myself, but I may be overlooking some other possible scenarios which were the reason for the elevated privileges in the first place.

Ron
Sep 8, 2008 at 8:44 AM

Another update:

Moved the block of code that checks the current users outside of the RunWithElevatedPrivileges block. However the "group.ContainsCurrentUser" still returned false. I iterated through the users and compared them with the current user. I had to use the string SPUser.LoginName for the comparison - the two SPUsers (one retrieved from spWeb.CurrentUser and one from roleAssignment.Member) were not *quite* identical. One had regionalsettings defined, and the other didn't. This is possibly also why the "group.ContainsCurrentUser" returns false too.

Below is my version of IsInRole() - Please let me know if I've done anything here that could cause problems elsewhere:-

    private bool IsInRole(SPWeb spWeb, string roleDefinitionName)
    {
        // Security checks: Fails if the user doesn't have Reader access (implemented
        // by SharePoint)

        // Check parameters
        if (spWeb == null)
            throw new ArgumentNullException("spWeb");

        // Verify that the web is in the site
        if (spWeb.Site.ID != m_anonymousSlkStore.SPSiteGuid)
            throw new InvalidOperationException(AppResources.SPWebDoesNotMatchSlkSPSite);

        bool isInRole = false;

        SPRoleDefinitionBindingCollection roleDefinitionCollection = null;
        try
        {
            // In some cases this fails and we must take an alternate approach.
            roleDefinitionCollection = spWeb.AllRolesForCurrentUser;
        }
        catch (SPException spException)
        {
            System.Runtime.InteropServices.COMException comException = spException.InnerException as System.Runtime.InteropServices.COMException;
            if (comException == null || comException.ErrorCode != unchecked((int)0x80040E14)) // Not the specific case we're looking for, rethrow
                throw;
            SPRoleAssignmentCollection myRoleColl = null;
            // Use a brute force iteration approach if the attempt to get AllRolesForCurrentUser fails with COMException 0x80040E14
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPSite site = new SPSite(spWeb.Site.ID))
                {
                    using (SPWeb web = site.OpenWeb(spWeb.ID))
                    {
                        myRoleColl = web.RoleAssignments;                      
                    }
                }
            });
            SPUser myUser = spWeb.CurrentUser;
            string uname = myUser.Name;
            string login = myUser.LoginName;
            foreach (SPRoleAssignment roleAssignment in myRoleColl)
            {
                SPUser user = roleAssignment.Member as SPUser;
                if (user != null)
                {
                    if (string.Compare(user.LoginName, HttpContext.Current.User.Identity.Name, true, CultureInfo.InvariantCulture) != 0)
                    {
                        continue; // the roleAssignment is for a different user
                    }
                }
                else
                {
                    SPGroup group = roleAssignment.Member as SPGroup;
                    if (group != null)
                    {
                        bool containsMyUser = false;
                        foreach (SPUser aUser in group.Users)
                        {
                            if (aUser.LoginName == myUser.LoginName)
                            {
                                containsMyUser = true; // the roleAssignment is for a group the user is not a member of
                            }
                        }
                        if (!containsMyUser)
                        {
                            continue;
                        }
                    }
                }

                foreach (SPRoleDefinition roleDefinition in roleAssignment.RoleDefinitionBindings)
                {
                    if (roleDefinition.Name == roleDefinitionName)
                    {
                        isInRole = true;
                        return isInRole;
                    }
                }
            }
        }

        if (roleDefinitionCollection != null)
        {
            foreach (SPRoleDefinition roleDefinition in roleDefinitionCollection)
            {
                if (roleDefinition.Name == roleDefinitionName)
                {
                    isInRole = true;
                    break;
                }
            }
        }

        return isInRole;
    }

Coordinator
Sep 8, 2008 at 8:44 PM
Would you be so kind as to upload a patch file as described in:  http://www.codeplex.com/SLK/Wiki/View.aspx?title=Checking%20In

This lets me review the code by looking only at the changes you have made and easily apply the code to compile/test.

Sep 9, 2008 at 3:06 AM

I'll give it a go Jay :)

OK - patch uploaded (id #1803). CPC complained about unversioned items when I created the patch - let me know if that means there's something else I should be doing.

Ron

Coordinator
Jun 29, 2009 at 9:48 PM

I've now found the cause of the original problem, it's a bug in SharePoint. I've blogged about it at http://blog.salamandersoft.co.uk/index.php/2009/06/sharepoint-bug-in-spweb-allrolesforcurrentuser/

Richard

SLK Co-ordinator