Approval Workflow Access Denied – How to fix it!

Updated 3/18/2015:

Turns out when you migrate from one domain to another, this error pops up yet again (even after running the migrate user commands)!  Another option of fixing this is to open *each* site collection and delete the workflows.  Then deactive and active the feature.  Then you can create sites again!

Yet again, another weird bug in SharePoint 2010 that I had to track down and fix this week!  What is this?  Here's what happened:

 My customer has a few workflows created to approve important documents.  All of a sudden, these workflows stopped working with an access denied error message.  In looking at the log files, you would see this  (it's rather long):

06/27/2012 06:40:02.96     w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     Access Denied. Exception: System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))     at Microsoft.SharePoint.Library.SPRequestInternalClass.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnAuthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkD…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …ayEndHour, Int16& pnMeetingCount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbstrSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pb…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …OverwriteMUICultures, Boolean& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag)     at Microsoft.SharePoint.Library.SPRequest.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnAuthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkDayEndHour, Int16& pnMeetingC…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …ount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbstrSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pbOverwriteMUICultures, Boolea…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           ftd0    Verbose     …n& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag).    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96     w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     Access Denied for /contoso/_layouts/IniWrkflIP.aspx?List={b713a4ad-747e-4980-ad1c-c420d3f588b7}&ID=1&TemplateID={3cf9d212-c83d-4486-9fce-ee8d00efacbc}&Source=blah StackTrace:    at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(HttpContext context)     at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(Exception ex)     at Microsoft.SharePoint.Library.SPRequest.OpenWeb(String bstrUrl, String& pbstrServerRelativeUrl, String& pbstrTitle, String& pbstrDescription, String& pbstrTitleResourceId, String& pbstrDescriptionResourceId, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, Guid& pguidScopeId, UInt32& pnA…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …uthorID, UInt32& pnLanguage, UInt32& pnLocale, UInt16& pnTimeZone, Boolean& bTime24, Int16& pnCollation, UInt32& pnCollationLCID, Int16& pnCalendarType, Int16& pnAdjustHijriDays, Int16& pnAltCalendarType, Boolean& pbShowWeeks, Int16& pnFirstWeekOfYear, UInt32& pnFirstDayOfWeek, Int16& pnWorkDays, Int16& pnWorkDayStartHour, Int16& pnWorkDayEndHour, Int16& pnMeetingCount, Int32& plFlags, Boolean& bConnectedToPortal, String& pbstrPortalUrl, String& pbstrPortalName, Int32& plWebTemplateId, Int16& pnProvisionConfig, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& pbstrThemedCssFolderUrl, String& pbstrAlternateCSSUrl, String& pbstrCustomizedCssFileList, String& pbstrCustomJSUrl, String& pbstrAlternateHeaderUrl, String& pbstrMasterUrl, String& pbstrCustomMasterUrl, String& pbs…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …trSiteLogoUrl, String& pbstrSiteLogoDescription, Object& pvarUser, Boolean& pvarIsAuditor, UInt64& ppermMask, Boolean& bUserIsSiteAdmin, Boolean& bHasUniquePerm, Guid& pguidUserInfoListID, Guid& pguidUniqueNavParent, Int32& plSiteFlags, DateTime& pdtLastContentChange, DateTime& pdtLastSecurityChange, String& pbstrWelcomePage, Boolean& pbOverwriteMUICultures, Boolean& pbMUIEnabled, String& pbstrAlternateMUICultures, Int32& puiVersion, Int16& pnClientTag)     at Microsoft.SharePoint.SPWeb.InitWeb()     at Microsoft.SharePoint.SPWeb.get_Title()     at Microsoft.SharePoint.SPSite.OpenWeb(Guid gWebId, Int32 mondoHint)     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.GetWebForWorkflow(SPWorkflow wf, SPWorkflowUserContext runAsUser)     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.get…    aed86901-9e96-4e1d-acac-32f5d8bba3d8
06/27/2012 06:40:02.96*    w3wp.exe (0x1314)                           0x2A00    SharePoint Foundation             General                           8gs1    Verbose     …_Web()     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.GetWebForListItemService()     at Microsoft.SharePoint.Workflow.SPWinOEWSSService.UpdateModerationStatus(Guid id, Guid listId, SPItemKey itemKey, SPModerationStatusType newModerationStatus, String comments)     at Microsoft.Office.Workflow.Actions.SetTaskProcessItemModerationStatus.DoUpdate(ActivityExecutionContext context)     at Microsoft.SharePoint.WorkflowActions.WaitForDocumentUnlockActivity.Execute(ActivityExecutionContext executionContext)     at Microsoft.Office.Workflow.Actions.SetTaskProcessItemModerationStatus.Execute(ActivityExecutionContext context)     at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext)     at System.Workflow.ComponentModel.ActivityExe…    aed86901-9e96-4e1d-acac-32f5d8bba3d8

Looking at the stack trace, you can see that the code is making a call to the GetWebForWorkflow method of the SPWinOEWSSService.  In reflecting this method, you will see that it can take one of three paths:

  • Execute as the system account
  • Execute as the workflow template author
  • Execute as the workflow template owner

The system account always has access, so it was obvious that this wasn't the path it was taking.  The owner seems to always be set to "-1" and no where in the massive amount of code that I reviewed did it ever get set to anything else.  That left the author as the most probably entry point.  When reviewing where the author comes from, you find a tangled set of code that really doesn't make much sense at all (hmm…SharePoint in all its glory).  After decoding the entanglement, I found that the author comes from some set of properties that end up in a object array for the SPWebWorkflow.  This array is built from a call to SPRequest.GetListItemWorkflowAsSafeArray.  This of course means that you can't go any farther into the code unless you get really fancy building a SPRequest wrapper (which can be done, but its really hard).  Because the properties are built in a somewhat OnDemand fashion, you don't really have any way of knowing when that code will be executed…and because I'm in production, I can't sit here and do IISRESET's all day long watching for some workflow related Stored Procedure via SQL Profiler.

So how did I figure out what was up with this?  Well, the hint was in the SPRequest method call name.  GetListItemWorkflowAsSafeArray.  ListItem?  Which implies the metadata for the workflow is in the list.  Which means the author info is also coming from the listlistitem.  So where do workflows get stored in a siteweb?  They get saved to the \_catlogswfpub library at the site collection root web.

In looking at this library through SharePoint Designer, you will see that when you first activate the workflow features, the files are created by THE USER who actually made the feature activation call (whether through the UI or stsadm or PowerShell).  Hmm, very poor design.

Why is it poor design?  Well, the author comes from the created field for the list item that has the workflow template defined.  In our case, this account was recently DISABLED.  When an account becomes disabled, you can't do JACK in SharePoint.  This includes executing the workflows that were built off the template in the first place. So what are your options?

  • Delete all the files, reactivate the workflows – YOU MUST DELETE THE FILES, if you do not, the files will stay with the created field still set to the old userId (If you have ever taken any of my course, you know rule number one is "SharePoint never deleted anything").  This means you'll still have the ACCESS DENIED error even after you deactivate and re-activate the workflows
  • Try to modify the CREATED and MODIFIED columns in the list to change the SPUser (I didn't try this, but it seems like it should work)

So, have you seen this?  Probably not.  Will every SharePoint farm on the planet eventually have this issue – YES!  You won't keep accounts around forever (unless you have a very poor security policy).  So it is worthwhile to review all your site collections and check to see who the workflow files are owned by.  Hopefully you have an SPAdmin or SPFarm account that you can use to enable the workflows with.  SO…NET NET:

Never enable workflow features as a regular user, always use some SharePoint account that will never be disabled

Yuk, yuk…yuk|
Chris

 

SharePoint Durable Urls Twitter Promotion!

As some of you know, I'm originally from Oklahoma, USA and lived in Seattle for 10 years.  In celebration of the Oklahoma City Thunder making it to the NBA Finals, ACS will be giving away 5 copies of our products for each Thunder Win!  The first 5 copies will be for the SharePoint Durable Urls product:

http://www.sharepointdurableurls.com/twitter.aspx

To enter, you simply need to re-tweet from the above twitter page!

Let's go OKC!
Chris

2012 SharePoint Jokes

Time for some more SharePoint jokes!

  • What did Office Word 2010 say to SharePoint 2010 Word Automation Services?
    • When can I retire?
  • There was a party going on in the content database, and several SharePoint groups were invited.  The pick-up question of the night was…
    • What site collection you from?
  • What percentage of code is Microsoft's after installing all 3rd party components to support your business?
    • <5%
  • A security firm recently asked SharePoint customers what was their major pain with SharePoint security?  The customer replied…knowing what permissions SharePoint decides to give you at any moment in time!
  • A customer recently was migrating from Oracle Portal to SharePoint 2010. After the migration, the customer asked, where'd all my features go?
  • How did the IT Pro reset the passwords on their SharePoint Farm?
    • They reinstalled everything!
  • What did one SharePoint user tell another user…?
    • Hey, if we yell enough, we can get SharePoint Designer access!
  • On average, how many SharePoint consultant firms till you find a good one?
    • Not including MCS?
  • What will you find if you look in the recycle bin?
    • A hole
  • What happens if you program Project Service Interface (PSI)?
    • Absolutely nothing
  • How many times does it take to setup Document IDs?
    • Lost count at 100…
  • How many customer actually use Document IDs?
    • One – Microsoft
  • What happens when you setup the content type hub?
    • You get lots of errors
  • How many SharePoint developers do you need to support your business units custom needs?
    • Half of China
  • How many SharePoint MVPs earned their MVP from posting on the MSDN Forums?
    • Wait…they know what the forums are?
  • What did the Oracle WebCenter customers tell Oracle after looking at Microsoft Pricing?
    • Why do you make us pay so much?
  • Why did Seattle lose the SuperSonics? 
    • Because SharePoint Designer is FREE and Baller isn't Jewish…and lattes cost $5 and Shultz is Jewish…and neither of them are from Oklahoma!

Bad, but oh so good…
CJG

How to remove blank comments on Append Changes to Existing Text

This is a rather simple issue, but an issue none the less.  It has been covered a few times before:

The issue is that it creates a blank comment every time someone edits a versioned list item.  The reason is that it *IS* actually adding text but it is an empty DIV tag:

<DIV></DIV>

When you set the column to be plain text, it doesn't add this empty DIV tag.  Ok, so just change to plain text and everyone is happy right?  Not quite.  When users connect to the list via the Outlook option, you get a very nasty set of HTML saved to the column (even in Plain text mode):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META NAME="Generator" CONTENT="MS Exchange Server version 08.01.0240.003">
<TITLE></TITLE>
</HEAD>
<BODY>
<!– Converted from text/plain format –>
</BODY>
</HTML>

Here's what you do in the event receiver to get rid of all this mess and have it work wether it is Plain Text or Rich Text:

class ContosoEventReceiverAppendChanges : SPItemEventReceiver
{
public override void ItemUpdating(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
Hashtable ht = new Hashtable();
foreach (DictionaryEntry o in properties.AfterProperties)
{
if (o.Value.ToString() == "<DIV></DIV>" || o.Value.ToString().Contains("<BODY> <!– Converted from text/plain format –> </BODY>"))
{
ht.Add(o.Key, o.Key);|
}
}
foreach (string key in ht.Keys)
{
properties.AfterProperties[key] =
"";
}}}

Powershell to deploy it:

$web = get-spweb http://sharepoint.contoso.com
$list = $web.lists["TaskTest"]
$def = $list.EventReceivers.add()
$def.Assembly = "Contoso.Common, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=a61cba8dcde3eaaa";
$def.Class = "Contoso.Common.ConotosEventReceiverAppendChanges";
$def.Name = "ItemUpdated Event";
$def.Type = [Microsoft.SharePoint.SPEventReceiverType]::ItemUpdating;
$def.SequenceNumber = 1000;
$def.Synchronization = [Microsoft.SharePoint.SPEventReceiverSynchronization]::Synchronous;
$def.Update();
$web.dispose()

Enjoy,
Chris

Follow me on twitter: @givenscj

Check out the previous blog post:
http://blogs.architectingconnectedsystems.com/blogs/cjg/archive/2012/05/30/Project-Server-2010-_2D00_-Moving-Project-Sites-and-deleting-the-old-PWA-causes-issues.aspx