Tuesday, November 22, 2011

Workflow in Duet Enterprise: concept + implications

One of the Duet Enterprise core capabilities is publishing SAP workflows into the realm of SharePoint sites. In this posting I outline the conceptual working, and what implications result from the Duet Enterprise workflow architecture. The information below is not entirely new, but builds upon blog material of Xiaosheng (Edward) Lu [SAP AG] and Kiki Shuxteau [Microsoft Corp].
This blog is earlier published on SAP Community Network Blogs

Prerequisites for exposing a SAP workflow via Duet Enterprise

  1. SAP ERP workflow; it does not matter whether a standard workflow, or a company-custom developed one. SAP BPM workflows are currently not supported for Duet Enterpise exposition.
  2. The SAP workflow must contain one or more discrete user interaction tasks.
  3. Duet Enterprise out-of-the-box supports User Decision interaction step. Interaction task with an Activitity Dialog are also supported, but require some manual work. Effectively you need to replace both the SAP GUI screenhandling plus propagate the dialog resultdata as outcome for the SAP workflow Activity Dialog task.

 

Conceptual working

The following steps are performed in the exposition of a SAP ERP workflow interaction task to SharePoint context:

  1. In the SAP ERP workflow a status change occurs that next requires user interaction to act upon, via a SAP Workflow Interaction Task (Decision Step or Activity Dialog). When the SAP Workflow reaches the Interation Task, a Duet Enterprise specific outbound handler is invoked from within the runtime context of SAP workflow execution. This outbound handler uploads an XML payload document, containing the workflow instance details, to the associated [by configuration] SharePoint workflow subsite. In the Duet Enterprise architecture, this XML payload is send from SAP ERP Workflow context via the SAP Document Publisher, which resides on the Duet Enterprise SAP Add-on. The SAP Document Publisher service on its turn invokes the SharePoint OBAWorkflowService, which is deployed in the SharePoint farm as part of Duet Enterprise installation.
  2. On the receiving SharePoint document library [in the workflow subsite] is a Duet Enterprise specific SharePoint workflow configured. This workflow is triggered on addition of new content in the document library. The workflow contains 1 user task to act from the context of SharePoint user interface upon the specific surfaced SAP interaction task.
  3. The decision made by the SharePoint enduser in the SharePoint workflow task, is propagated to the SAP ERP workflow by means of a taskflow BDC entity. Duet Enterprise specific SAP webservices are herefore invoked via SharePoint BCS. These SAP webservices next invoke a Duet Enterprise specific inbound handler to receive and process the exchanged decision details, and propagate it as outcome of the user interaction task in the SAP ERP workflow.

Implications

  • Duet Enterprise provides default Function Modules for both inbound and outbound handlers. By design, these are limited to generic handling of SAP workflow. In case the SAP ERP workflow decision task includes context information that is workflow specific, it is required to build a custom outbound handler. Also in case the result datacontainer of the decision task contains context data besides or instead of a simple decision outcome, it is required to build a custom inbound handler to process that received data from SharePoint task into the SAP workflow.
    • In concrete reality, SAP workflows more often than not involve acting upon workflow specific data. In all such situations it is thus required to overload the default function module with a custom implementation, for surfacing that specific data outside the boundaries of the SAP workflow.
  • The default Duet Enterprise outbound handler S_OSP_WF_PAT_DEFAULT_CH_OB handles the publishing of XML payload to SharePoint. Even in case of a custom outbound handler, it must therefore still be utilized in the Duet Enterprise workflow publication. More specifically, the S_OSP_WF_PAT_DEFAULT_CH_OB outbound handler must always be the last invoked outbound handler in the SAP workflow outbound handlers pipeline. The order in which outbound handlers are invoked is configured within SAP ERP, not in the SCL (aka Gateway).
  • The default Duet Enterprise inbound handler S_OSP_WF_PAT_DEFAULT_CH_IB handles the receiving of Duet Enterprise taskflow entity from SharePoint BCS. It must therefore always be present as the first invoked inbound handler in the SAP workflow inbound handlers pipeline.

Saturday, November 19, 2011

Alternative ways to programmatic read contents of External List with filtered view

In order to export the displayed contents of a BCS External List to Excel (see previous post), I first have to programmatically retrieve the contents in custom code. The External List has a filter applied to it's Finder method, set via the default SPView:
My first thought was therefore that below code should retrieve the same filtered external data as when rendering the BCS External List on a SharePoint page:
However, GetItems returns an empty collection. Upon debugging the called GetNotifications webservice method, I discovered that the filter-param always has value ‘null’. To me unclear why, since it has been set in the DefaultView.
However, although this approach works; it left me rather dissatisfied. Conceptually I want to export the contents of the same (External) List that I already have provisioned on a SharePoint page; so why should I have to dive under the BCS hood to get the same contents? Also, this code is very much aware / coupled to BCS API, while the 'Export SPList to Excel' functionality on itself is general. Thus, with some ample time available, I decided to analyze the way in which the standard XsltViewWebPart is issuing the external data retrieval – using JetBrains DotPeek reflector tool (as .NET Reflector is no longer free of licensee charge). It appears that this is done slightly different as my original attempt:
Not only is the code LOC of this far less, but also this code is general; applicable for both regular SharePoint lists as BCS External Lists.

Friday, November 18, 2011

Excel 2010 Protected View hinders browser-opening of downloaded .xlsx file

An user requirement in one of our SharePoint 2010 projects is to export at any moment the displayed contents of an External List (with content originating from SAP ERP, retrieved via SharePoint BCS connecting to BAPI based web services) to an offline file. The functional rationale is version-administration for history and auditing purposes. The SharePoint platform supports this out-of-the-box for regular Lists, by the Export into Excel functionality. However, not so for BCS External Lists. But you can realize it yourself via some custom code. First retrieve the External List contents, and next construct a .xlsx file via Open XML SDK. The .xlsx file is generated server-side in memory, and send to the browser as HttpResponse content. The end-user can next either open the file, or save it somewhere at client-side:
Strange thing I noticed was that when saving the file, that saved file can next be opened successfully. But when instead choose to directly open the file, Excel 2010 displays the error message “The file is corrupt and cannot be opened”.
This must be a client-side issue; the server-side is not aware of the context in which the client-side handles the received HttpResponse (Note: via Fiddler I even analyzed that the HttpResponse contents were identical).

The resolution is hinted at in the File Download window, by the trust-warning about internet downloaded files. The default Excel 2010 TrustSettings are to distrust all downloaded content from non-trusted locations. To validate this I unchecked in Excel 2010 the default settings (via File \ Options \ TrustCenter \ TrustCenter Settings \ Protected View):
This helps, Excel 2010 now direct opens the downloaded file.

Thursday, November 10, 2011

Inconvenient configuration of Forms-Based Authentication in SharePoint 2010

For an extranet we aim to utilize Forms-Based Authentication to authenticate external users. In SharePoint 2010 this means that you have to apply the Claims-Based authentication model. And next set up the SharePoint webapplication configuration for the Membership provider that will be used for FBA. This is where things can be a little confusing. In fact you have to configure FBA membership on 3 different locations, and it is essential that all 3 are in sync:

1. In Central Admin

Configure Web Application \ Authentication Providers; here you specify the name of the Membership- and RoleProviders used in FBA context within the web application

2. Security Token context

In the SharePoint 2010 service applications architecture, membership handling is delegated to the SecurityToken service application. This is a major difference wrt SharePoint 2007 architecture, in which the individual webapplication process themselves handle the membership handling. Direct consequence for configuration is that you need to specify in the web.config of the SecurityToken service application all the membershipproviders and roleproviders that are used in the SharePoint farm.
The SecurityToken service application directory is located at: 14hive\WebServices\SecurityToken

3. Webapplication context

Finally, despite that membership handling is within SharePoint 2010 done via the independent SecurityToken service application; you may still need to add the membershipprovider to the web.config of the individual webapplication. This is required in case you want to be able to use the standard PeoplePicker for selecting credentials via the FBA membershipprovider. Besides adding the ‘membershipprovider’ node, you then also have to set the peoplepicker directing to that membership provider.
What if the 3 locations are not in sync? I evaluated the different inconsistent configurations:

1. Name entered in Central Admin does not match with name in SecureToken’s web.config

In this case, SharePoint Identity handling cannot get an handle to the configured MembershipProvider. When someone tries to log in via Forms-Based Authentication, SharePoint Identity handling will report the displayed exception.
Note, to have the 'Cannot get Membership Provider' exception details displayed you already need to make a change to the default settings within SecurityToken web.config. Namely set attribute includeExceptionDetailInFaults of ServiceDebug to true. Without this, only a general error is displayed: The server was unable to process the request due to an internal error

2. Missing MembershipProvider in Central Admin

Well, you cannot actually forget to fill them in if you selected ‘Enable Forms Based Authentication’. But you could per accident forget to select that option.
The result of this is more a functional error: the forms-based authentication is now missing as option to logon to the web application.

3. Missing or different named MembershipProvider in web application’s web.config

In this case you can still logon to the webapplication: either via Windows Authentication or via FBA. See above, membership is namely handled by SecurityToken service application, and not by the webapplication self. This can be a bit confusing at first. The result of the configuration error is noticed within the application self, when you try to search credentials from the FBA-Membership provider via the PeoplePicker. Strange enough the PeoplePicker is aware of the configured FBA-membership, but apparently cannot include it in it’s search space.

4. Mismatch PeoplePicker (incorrect) versus named MembershipProvider in webapplication’s web.config

In this case strangely enough, PeoplePicker is able to use the Membership provider, that is if it is consistently named wrt SecurityToken web.config (otherwise issue 1).
At first I could not detect any PeoplePicker malfunction as result of this configuration mismatch. I could still find all credentials in the membershipprovider, also via wildcarding.
Only when I on purpose sabotaged the wildcarding, I could see the effect.
So it appears that for wildcard search the ‘%’ is already somewhere set as default for all Membership providers; and that you only have to override this in case your Membership provider uses a different wildcard-pattern.
Personally, I find it better to always be explicit; and thus also explicitly specify ‘%’ as wildcharacter for each Membership provider that is valid for your application. But it is optional, and others might differ with me on this…

Friday, November 4, 2011

Programatically open SPSite using Windows Credentials

In a Proof of Concept I employ a SPListMembershipProvider for forms-based access into (sub)sites. For the PoC, a SPList in the rootweb is utilized as User Administration. In SharePoint 2010 architecture, Claims-Based authentication is handled by SecureToken service application. In the local development image, the STS service application may run under the same Application Pool account as the SharePoint webapplication. But this is not recommended, thus not to be expected within a real farm setup. As result, it is not possible to directly access from within runtime STS context the list in the external SPSite.
SPSecurity.RunWithElevatedPrivileges cannot help here; that only be used within the same application pool context. Instead the proper way is to open the SPSite in the external SPWebApplication via the credentials of a SPUser in that site; e.g. that of a service account. Problem is that the SharePoint API does not directly provide a way to open a SPSite with Windows Credentials. You can open a site under the credentials of a SharePoint user, but you need the SPUserToken of the user for this. And guess what, you can only determine that token when within the context of the site. Talking about a chicken-egg situation.
However I came up with a manner to get out of this loop. It consists of a 2-steps approach: first programmatically impersonate under the credentials of the service account, open the site, determine the SPUserToken of the site’s SystemAccount, and undo the impersonation; second apply the SPUserToken to (re)open the site under the authorization of the site’s SystemAccount. Since Windows Impersonation is a resource intensive operation, cache the SPUserToken in memory so that the impersonation is only initially required within the process lifetime.

internal static SPUserToken SystemTokenOfSite(Guid siteId)
{
    string account, pw, domain;
    RetrieveCredentialsFromSecureStore(<AppId>, out domain, out account, out pw);

    ImpersonationHelper _impersonator = new ImpersonationHelper(account, domain, pw);
    try {
        _impersonator.Impersonate();

        SPSite initialAccessIntoSite = new SPSite(siteId);
        return initialAccessIntoSite.SystemAccount.UserToken;
    } finally {
        _impersonator.Undo();
    }
}

...
if (sysToken == null)
{
    sysToken = SecurityUtils.SystemTokenOfSite(memberSitesToId[websiteIdent]);
}

SPSite site = new SPSite(memberSitesToId[websiteIdent], sysToken);