Nintex Custom Actions permissions – Understanding RunWithElevatedPrivileges

When trying to do a simple System.IO.File.Copy inside the context of a Custom Nintex Action I wrote, I found that I couldn’t get the file to copy to a particular Windows Server File Share. Even though I had assigned the user name that was running the Nintex Workflow Read/Write permissions on the share, it would fail with an error indicating lack of access to that folder.

It worked ok if I set “Everyone” with Read/Write permissions. Reviewed the great article on Windows Server 2008 File Share setup at http://www.techotopia.com/index.php/Configuring_Windows_Server_2008_File_Sharing , still it was clear the File Share was just net configured right.

The source of the issue is that in running the File Copy code inside a RunWithElevatedPrivileges block, it is actually using a different account than the one running the workflow. Here is the Nintex Execute Activity function in question:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			NWWorkflowContext ctx = NWWorkflowContext.GetContext(
			   this.__Context,
			   new Guid(this.__ListId),
			   this.__ListItem.Id,
			   this.WorkflowInstanceId,
			   this);

			base.LogProgressStart(ctx);
				SPSecurity.RunWithElevatedPrivileges(() =>
				{
					if (!Directory.Exists(ctx.AddContextDataToString(OutputPath)))
						Directory.CreateDirectory(ctx.AddContextDataToString(OutputPath));
					File.Copy(ctx.AddContextDataToString(SourcePath) + ctx.AddContextDataToString(strFileName),ctx.AddContextDataToString(OutputPath) + ctx.AddContextDataToString(strFileName),true);
				});
			}
			catch (Exception ex2)
			{
			    EventLog.WriteEntry("CopyFile Create Exception - Source File [" + SourcePath + strFileName + "] Output File [ " + OutputPath + strFileName + "]", ex2.Message + ", StackTrace: " + ex2.StackTrace, EventLogEntryType.Error, 9999);
			}
			base.LogProgressEnd(ctx, executionContext);
			return ActivityExecutionStatus.Closed;
		}

The thing to remember regarding this method call is that the account NAME that is being used is “SHAREPOINT/SYSTEM” and this does not resolve to an actual domain account. Now the only time this becomes and issue is when one is referring to Windows resources outside of SharePoint. Again this is only intended to be used for resources within SharePoint. If your function code is say calling even a SharePoint web service and one sets the web service “Credential” to “System.Network.CredentialCache.DefaultNetworkCredential” that the service call will in fact fail because IIS will not be able to resolve the domain account “SHAREPOINT/SYSTEM”. If the case is one needs to access external data then consider using a Secure Store credentials.

I’ve always found that the nuances of RunWithElevatedPrivileges cause much confusion as well as some weird and wonderful behavior if not properly understood. While the documentation here does a good job of explaining how and when to use it, it’s missing a key piece of information and that’s how the mechanism actually works.

Behind the scenes, RunWithElevatedPrivileges impersonates the identity of the current thread. In effect, this means that the delegate will run under the context of the application pool account, in the case of the code being called in the W3P process, or in the context of the SPTimerv4 service, in the case of the code being called in a workflow, timer job or anything else that’s kicked off using the timer. If the code is running in a console application or some other user-initiated app, the delegate will be kicked off using the context of the user who started the application. (Which would be the default behavior anyway).

When using workflows, bear in mind that the workflow may start running under W3P but continue executing under owstimer (SPTimerV4) depending on what it’s actually doing. In this case a delegate executed using RunWithElevatedPrivileges would not neccesarily yeild the same result.

PS nifty trick if you’ve forgotten about it: just add a $ to the end of the share name (rendering it accessible but invisible when people browse the shares).

SpSite in Nintex Workflow Custom Action

So today I needed to get the URL of the current SharePoint site my Nintex Custom Action Visual Studio solution is executed under, using the SPSite class. Problem was, was not working on the server of the SPSite. Nor would I want to have to install Visual Studio on the production instance where the solution was to live.

Makes sense, just need to grab the httpcontext. SPContext.Current.Site seemed to fit the bill, but alas, it did not work. Lost some time hunting for a solution to the following error:

Could not create activity of type 'CustomActions.MyCustomActivity'. System.IO.FileNotFoundException: The Web application at http://xxx.xxx.com could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.
at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite, SPUserToken userToken)
at Microsoft.SharePoint.SPSite..ctor(String requestUrl)

There’s a number of possible causes and it took going through some pain in order to resolve which route to take. The standard contingencies are explained quite well on http://www.thorntontechnical.com/tech/sharepoint/new-spsite-filenotfoundexception. None of those, possibly valid error causes, solved my beef.

In the end I found that we don’t actually need to be local with VS on the SharePoint server the solution is compiled against- it was just a matter of finding out how to reference the application context: the Nintex way. In this case, it was being handled by Nintex and the method used was not seemingly explained in the SDK or documented. Looking a bit deeper it became clear the application context was being fed in by Nintex.

Working from the DeployWorkflowActionWithFeature example in the Nintex Workflow 2010 SDK, we can see that in the EventLogAdapter.cs in the project, the context is initially set up in the following function:

public override CompositeActivity AddActivityToWorkflow(PublishContext context)
        {
            EventLogActivity activity = new EventLogActivity();

            Dictionary<string, ActivityParameterHelper> parameters = context.Config.GetParameterHelpers();

            parameters[Parameter_Message].AssignTo(activity, EventLogActivity.MessageProperty, context);
            parameters[Parameter_EventSource].AssignTo(activity, EventLogActivity.EventSourceProperty, context);

            activity.SetBinding(EventLogActivity.__ContextProperty, new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__context));
            activity.SetBinding(EventLogActivity.__ListItemProperty, new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__item));
            activity.SetBinding(EventLogActivity.__ListIdProperty, new ActivityBind(context.ParentWorkflow.Name, StandardWorkflowDataItems.__list));

            ActivityFlags f = new ActivityFlags();
            f.AddLabelsFromConfig(context);
            f.AssignTo(activity);

            context.ParentActivity.Activities.Add(activity);
            
            return null;
        }

That function sets up the __context for use in EventLogActivity.cs as follows:

public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(EventLogActivity));

Now that we have that rolling, we can use the __Context object with SPSite to get our current application URL.
SO, instead of using this:

SPSite site = new SPSite(“http://mysite/sitename”);

Use this:

SPSite site = new SPSite(this.__Context.Site.Url);

You now have the basis to perform all the wonderful SharePoint API stuff you need to do within the context of the Nintex Custom Action.

Business Hours in Nintex Workflows

In Nintex Workflows you can define when the workflow will execute in the context of the the business entities “working hours”.

When refering to business hours, these are the hours of operation defined in Site Settings > Regional Settings of the site where the Nintex Workflow resides. You can also set your public holidays which can be included through Site Settings > Manage Holidays

Also – a special tip – (thanks Colin for clarity) –  although the Pause Workflow action can be set to 1 minute in it’s settings, it will actually never execute in less than 5 intervals as it is dependent on the SharePoint time job. Important to remember as you may be wondering why your workflow takes ages to finish if you try and insert <5 minute intervals. It is actually 5 minutes + the duration of the workflow sequence + the minute interval you selected.