Impersonating users inside UDFs

In a previous post I described how UDFs can figure out who the user running them is.

Luis already touched on how to impersonate a user, but has a very serious (but very easily fixable) bug in it. (Edit: Luis fixed the bug inside his post)

In this post, I will add one important notion - the impersonation of a user. A UDF can impersonate a user so that the code it runs will, from the systems point of view, run as if the user is running it. This is especially useful when accessing databases or other network resources that require an authenticated user.

To do this, the caller must use the Thread.CurrentPrincipal property to get the Identity that is running the UDF. If the ReturnsPersonalInformation property of the UdfMethod attribute is set, this Identity object will be a WindowsIdentity. A WindowsIdentity class has the ability to impersonate a user. This is done by calling the aptly named Impersonate() method. Once inside an impersonation context, the code will run under the user account.

The following piece of code shows how to do this - the Impersonate() method declared in the class does all the work needed for impersonating the user - returning a disposable WindowsImpersonationContext which, when disposed, will exit the impersonation context. When used inside a using statement, this guarantees proper roll-back of the impersonation.

using System;

using System.Collections.Generic;

using System.Text;

using System.Security.Principal;

using System.Threading;

using Microsoft.Office.Excel.Server.Udfs;

namespace MyUdfLibrary

{

    [UdfClass]

    public class MyUdfClass

    {

        private WindowsImpersonationContext Impersonate()

        {

            WindowsIdentity wi = Thread.CurrentPrincipal.Identity as WindowsIdentity;

            if (wi == null)

            {

                throw new InvalidOperationException("Could not get a windows identity.");

            }

            return WindowsImpersonationContext context = wi.Impersonate();

        }

        [UdfMethod(ReturnsPersonalInformation = true)]

        public string ReturnInformation(int param1)

        {

            using (WindowsImpersonationContext context = Impersonate())

            {

                string result = param1.ToString();

                // Access a database, do whatever the heck you want.

                return result;

            }

        }

    }

}

The code first casts the identity that's on the thread into a WindowsIdentity. If that succeeds, the helper method goes and calls the Impersonate method, returning the resutl.

The UDF itself then simply uses a using statement to run the appropriate code.

If the UDF did not have the ReturnsPersonalInformation set to true, the attempt to cast the identity to a WindowsIdentity will fail, returning null, causing an exception to be thrown.