Wesley Bakker

Interesting things I encounter doing my job...

  • Solving WmiApRpl and BITS errors with SharePoint 2013 on Windows Server 2012

    I have been searching for a way to get rid of some performance counter errors (WmiApRpl and BITS) on my SharePoint 2013 installation for a while but couldn't find the answer. Today I decided to have a look with Process Monitor and finally found a solution.

    The w3wp process tries to access to registry keys but does not have the permissions.

    After granting the WSS_WPG group full control(you probable can get away with a little less) to the following registry keys, the errors went away.

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BITS\Performance
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WmiApRpl\Performance

    And that's it!

    Cheers,

    Wesley

  • SPItemEventReceiver Bug

    Normally when you inherit a class, and override any of the methods, you call the base method, just to be sure you do not interfere with the inner workings of the class. There is a bug however in the SPItemEventReceiver class that always changes the Status to Continue, even if I decided it should stop and cancel. TMHI this is simply a bug. Especially since the SPEventReceiverStatus.Continue enum value is 0 and thus the default value.

  • Microsoft Certified Master SharePoint 2010

    Yesterday I've received the news that I've successfully completed all four exams that come with the MCM program. This actually makes me an MCM as of now which is still somewhat unreal to me. I've had to spent three weeks away from home strugling through the massive amount of information. The lectures were awesome, and I really learned to know a lot of good SharePoint specialists.

  • Get Datepart in SharePoint Designer Workflow

    Yesterday someone asked me how to get the datepart (f.e. the year of a date) inside a SharePoint designer workflow. I thought it would be some default action in the "Utility Actions" group, but I thought wrong. There is no such pretty common action. This post however shows a simple technique to get datepart Year, Month and Day.

    How to 

    First you have to start by asigning a new workflow string variable to a date value, and make sure to select the ISO Formatted option for the return field as parameter. This will set the string variable to something like 2010-12-28 according to the ISO format(yyyy-mm-dd).

    We can now use the Utility Actions for string variables to extract our Year, Month and Day.

    Screenshot

    Cheers,

    Wes

  • Framed Office Web Apps SharePoint 2010

    In a project I'm working on we wanted to use Office Web Apps in SP2010 to preview selected documents in the browser. To do so we've created a very simple web part that renders an I-Frame with the URL set to one of the Office Web Apps urls depending on the document extension. Unfortunately the X-Frame header, that is added by the Office Web Apps service, prevents Internet Explorer to render the documents in an I-Frame! To solve this we've create a very simple HttpModule that checks for the header and changes the value from "DENY" to "SAMEORIGIN". This post simply shows the code for such a module that enables previewing of documents with Office Web Apps inside an I-Frame

    The code

    /// <summary>
    /// The XFrameOptionsModule loosens the x-frame policy from DENY to SAMEORIGIN
    /// </summary>
    public class XFrameOptionsModule : IHttpModule
    {
        private const string XFrameOptionsHeaderName = "X-FRAME-OPTIONS";
    
        /// <summary>
        /// Initializes a new instance of the <see cref="XFrameOptionsModule"/> class.
        /// </summary>
        public XFrameOptionsModule()
        {
        }
    
        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }
    
        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
        public void Init(HttpApplication context)
        {
            context.PreSendRequestHeaders += ChangeXFrameOptionsHeaderToSameOrigin;
        }
    
        /// <summary>
        /// Changes the X-Frame-Options "DENY" header to "SAMEORIGIN".
        /// </summary>
        /// <param name="sender">The HttpApplication that triggers the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void ChangeXFrameOptionsHeaderToSameOrigin(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;            
            HttpResponse response = application.Response;
                
            string headerValue = response.Headers[XFrameOptionsHeaderName];
            if (headerValue != null && headerValue.Equals("DENY", StringComparison.OrdinalIgnoreCase))
            {
                response.Headers[XFrameOptionsHeaderName] = "SAMEORIGIN";
            }
        }
    }

    All you have to do now is to add this module to the web.config using a SPWebConfigModification inside a feature receiver.

    Cheers,

    Wes

  • Missing server reference in SharePoint 2010

    Unfortunately the "missing server references" health rule in Central Admin Health Monitoring does not display the URL were these uninstalled web parts reside. So to get these pages you'll have to execute some SQL against the mentioned content database. Because this is something that tents to come back regularly on development machines I've created a little SQL script to speed up the process of finding the url's you'll need to delete the web parts.

    -- Use the Specify Values for Template Parameters 
    -- command (Ctrl-Shift-M) to fill in the parameter 
    -- values below.
    
    
    USE 
    
    DECLARE @className nvarchar = ''
    DECLARE @assemblyName nvarchar = ''
    
    
    SELECT DISTINCT pages.DirName + '/' + pages.LeafName + '?contents=1' as Page
    FROM
    	dbo.AllWebParts wp
    	JOIN dbo.AllDocs pages on pages.SiteId = wp.tp_SiteId
    						AND pages.Id = wp.tp_PageUrlID
    WHERE
    	wp.tp_Assembly like '%' + @assemblyName + '%' AND
    	wp.tp_Class	 like '%' + @className + '%'
    

     

    Cheers,

    Wes

  • WorkflowAction and the Secure Store

    CallWebServiceWorkFlowActionI have been working on this custom workflow action which allows you to post data to another servicer. This can be used to send messages to a web service for example. One thing I really wanted to have in there is security. And with security I mean the Secure Store. It is however very difficult to use the Secure Store from inside a workflow action.

    Impersonation

    Although SharePoint actions are so called “impersonated” to the initiator of the user, the process really runs under its own account. Depending on how busy your server is, and the type of workflow is executed by the SPUserCodeHost, OWSTimer or W3WP process. And your code actually runs under the account of that process. The so called “impersonation” is for SharePoint actions only. This is accomplished by handing out the SPContext of the initiating step to the workflow.

    Secure Store

    Secure Store stores the credentials for users or groups and only while running under the user, or group account, can you get these credentials out of the secure store and that’s great of course, but that’s also where the trouble starts. We have no way of impersonating the actual initiator of the workflow and thus no way to get the credentials per user. One option would be to get a ticket in the Initialize method and use that ticket during the Execute method to retrieve the user credentials and yes this does work, most of the time. Because most of the time, the Initialize method will run inside the W3WP process which has a HttpContext which in turn has a WindowsIdentity we can use to impersonate. Unfortunately, if the server gets busy, the Initialize method might just as well run inside the OWSTimer process. Another problem with the ticketing system is that tickets are valid for a specified amount of time only. You should think in minutes instead of hours, but workflows well… they sometimes take a few days or weeks before they finally arrive at your workflow action.

    private static ICredentials GetSecureStoreCredentials(string applicationId, SPServiceContext context) {
        string username = String.Empty;
        string password = String.Empty;
    
        ISecureStoreProvider provider = SecureStoreProviderFactory.Create();
        if (provider == null) {
            throw new InvalidOperationException("Unable to get an ISecureStoreProvider");
        }
    
        ISecureStoreServiceContext providerContext = provider as ISecureStoreServiceContext;
        providerContext.Context = context;
    
        using (var credentials = provider.GetCredentials(applicationId)) {
            foreach (var credential in credentials) {
                switch (credential.CredentialType) {
                    case SecureStoreCredentialType.UserName:
                    case SecureStoreCredentialType.WindowsUserName:
                        username = credential.Credential.ToClrString();
                        break;
    
                    case SecureStoreCredentialType.Password:
                    case SecureStoreCredentialType.WindowsPassword:
                        password = credential.Credential.ToClrString();
                        break;
                }
            }
        }
    
        return new NetworkCredential(username, password);
    }

    Sample code for retrieving network credentials from secure store

    Careful

    One thing I would really like to stress out is that you really should not store the credentials in workflow variables during Initiate. Workflows can get stored anywhere (depending on the implementation of the WorkflowPersistenceService) and probably not secure. The other problem is that these credentials might not be valid anymore by the time your custom action executes.

    What’s the solution?

    There simply is no solution. The only thing you can do is create a so called Application Account (=Group account used by all users) in Secure Store and add the service accounts of the SPUserCodeHost, OWSTimer and W3WP processes in there. Problem is that every workflow action with the same Secure Store ApplicationId has to use the same credentials.

    Cheers,

    Wes