Thursday, December 9, 2010

Using SharePoint 2010 Secure Store Service Object Model

The secure store service in SP2010  is a great new feature which enables the BDC (Business Data Connectivity) service to connect to external resources. The secure store service along with BDC are consider two components of SharePoint’s Business Connectivity Services. You can read more about these services here Overview. It also can be used by your own custom SharePoint solutions to access external resources such as web services. For example, there are times when your solution may need access to external resources on another domain, therefore you would need to map the current user to credentials stored for that external resource. Your solution may also want to redirect users to a custom credential page to have user’s enter credentials for other applications, thus eliminating the need to prompt them every time they try accessing an external application. In this post I will show you how to set and get credentials for users from the secure store service object model. In addition, I will show you how to use stored credentials to access a web service. I have also put together a class that contains all the code in this posting, along with code for other secured stored service tasks, such as creating different types of secured stored applications and deleting credentials. The code can be downloaded from here: SecureStoreManagement.zip

The secured stored service provides two basic types of applications, group and individual. Group type applications are used to assign one set of credentials to groups and individual users. An individual type application is used to store one set of credentials for each individual user. You can also create a group or individual ticketed type applications. The ticketed type of application gives the ability to issue tickets to obtain credentials that will expire or timeout. These are useful for  more secure types of external applications. Finally, you can also create group or individual restricted type applications. The restricted application type only allows fully trusted code to obtain credentials. The examples, below deal with the individual type application. The classes used in the examples can be found in the Microsoft.BusinessData.dll and Microsoft.Office.SecureStoreService.dll.

 

Setting user credentials

The biggest problem that I ran into when using the secure stored service object model was trying to determine which credential corresponded with what field or parameter in the external resource. When you create a secured store application you are allowed to create up to 10 fields. You set each field’s name and credential type (Windows User Name, Windows User Password, Generic, PIN …). However, when using the object model you must use two different collections so you know which values you are setting or getting. You must use the collection of TargetApplicationField and the collection of ISecuredStoreCredential. In an individual type of application each set of credentials must be associated with a SecureStoreService claim, which you can create from a user’s login. The example below takes a user name, password, domain name and a user login to create the credentials and add them to the SecureStoreCredentialCollection in the same order as the TargetApplicationFields collection. This ensures that you can retrieve certain credentials from the SecureStoreCredentialCollection for a given TargetApplicationField. Finally, when creating the credential you must store the value as a System.Security.SecureString. The downloadable code contains the simple code to do this.

public static void SetUserCredentials(string userName,
    string userPassword,
    string domain,
    string targetApplicationID,
    string userLogin)
{      
            SPClaim claim = SPClaimProviderManager.CreateUserClaim(userLogin,
                SPOriginalIssuerType.Windows);
            SecureStoreServiceClaim ssClaim = new SecureStoreServiceClaim(claim);
            SPServiceContext context =
            SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default,
            SPSiteSubscriptionIdentifier.Default);

            SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
            ISecureStore iss = ssp.GetSecureStore(context);

            IList<TargetApplicationField> applicationFields =
                iss.GetApplicationFields(targetApplicationID);

            IList<ISecureStoreCredential> creds =
                new List<ISecureStoreCredential>(applicationFields.Count);

            using (SecureStoreCredentialCollection credentials =
                new SecureStoreCredentialCollection(creds))
            {

                foreach (TargetApplicationField taf in applicationFields)
                {
                    switch (taf.Name)
                    {
                        case "Windows User Name":
                            creds.Add(new SecureStoreCredential(MakeSecureString(userName),
                                SecureStoreCredentialType.WindowsUserName));
                            break;

                        case "Windows Password":
                            creds.Add(new SecureStoreCredential(MakeSecureString(userPassword),
                                SecureStoreCredentialType.WindowsPassword));
                            break;

                        case "Domain":
                            creds.Add(new SecureStoreCredential(MakeSecureString(domain)
                                , SecureStoreCredentialType.Generic));
                            break;
                    }
                }

                iss.SetUserCredentials(targetApplicationID, ssClaim, credentials);
            }

}

Getting user credentials

Getting a user’s credentials for a particular application is very straight forward. The code below takes an application ID and get’s the current users credentials. This method works for both group and individual type applications. The key thing to note is that it returns the credentials of the  currently  logged in user. If you are hoping to obtain the credentials for another user will you will be out of luck. The object model has no methods to retrieve credentials for other users. The internal code uses the current thread’s Identity to lookup the credentials in the secure store database. Now if you were able to somehow change the identity of the thread, then that identity also has to be logged in. There should be no need to impersonate another user when you can just easily map credentials to groups and individual users.

public static SecureStoreCredentialCollection GetCredentials(string targetApplicationID)
{
    SecureStoreCredentialCollection credentials = null;
    SPServiceContext context =
    SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default,
    SPSiteSubscriptionIdentifier.Default);

    SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
    ISecureStore iss = ssp.GetSecureStore(context);
    credentials = iss.GetCredentials(targetApplicationID);
    return credentials;
}

 

Using user’s secure store credentials

Now you can set and get credentials. So how do you use them? The code below shows how to get the current user’s credentials for an external application and create new credentials to call a web service. You can have many credentials but your code needs to know which ones to map to what fields to effectively use them. The code will take an application ID and get the credentials and using the list of TargetApplicationFields will map the values to variables by using the index position of the TargetApplicationField in the list, which will correspond to the same position in the list of ISecureStoreCredential. The new NetworkCredential then can be used to call a web service. I chose to use it with the SharePoint Lists web service to return the schema of a list. The secure store credential is stored as a System.Security.SecureString so you must translate it.

public static void UseSecureStoreCredentials(string targetApplicationID)
{

    listservice.Lists listsProxy = new listservice.Lists();
    listsProxy.Url = "http://basesmc2008/_vti_bin/lists.asmx";
    listsProxy.UseDefaultCredentials = false;
    string userName = string.Empty;
    string userPassword = string.Empty;
    string domain = string.Empty;

    using(SecureStoreCredentialCollection ssCreds =
        GetCredentials(targetApplicationID))
    {

        IList<TargetApplicationField> applicationFields =
            GetTargetApplicationFields(targetApplicationID);

        if (ssCreds != null && ssCreds.Count() > 0)
        {
            foreach (TargetApplicationField taf in applicationFields)
            {
                switch (taf.Name)
                {
                    case "Windows User Name":
                        userName =
                            ReadSecureString(ssCreds[applicationFields.IndexOf(taf)].Credential);
                        break;

                    case "Windows Password":
                        userPassword =
                            ReadSecureString(ssCreds[applicationFields.IndexOf(taf)].Credential);
                        break;

                    case "Domain":
                        domain =
                            ReadSecureString(ssCreds[applicationFields.IndexOf(taf)].Credential);
                        break;

                }
            }

            NetworkCredential externalCredential =
                new NetworkCredential(userName, userPassword, domain);

            listsProxy.Credentials = externalCredential;

            XmlNode listNode = listsProxy.GetList("shared documents");
        }
    }

}

 

Storing it all up

The secure store service is one component of the SharePoint’s Business Connectivity services and is used to enable integration of external resources with Microsoft Office. Here I have shown how your own solutions can leverage this service. Microsoft could have made this object model better by giving developers the ability to navigate the credentials without having to have the target application fields. However, having a central service to store, retrieve and manage external application credentials can enable SSO solutions between SharePoint and CRM systems. It can help you with NTLM “double hop” issues where credentials cannot be transferred across more than one computer boundary. The secure store service provides a way to store credentials securely rather than hard coding them in code or configuration files. SP2010 is making it easier to create more sophisticated enterprise solutions.

3 comments:

Jon Smith said...

Thanks, this is exactly what I have been looking for. All the code required in one simple to follow tutorial.

Anonymous said...

This is awsome, thank you so much

Anonymous said...

This is all good and well, but don't you run into the exact same problems using SecureString was meant to SOLVE by transferring the components of the credential to strings before creating the NetworkCredential instance in your "Using user’s secure store credentials" example? I'm glad to see SecureStrings being used for SecureStore, but I have yet to see the COMPLETE solution...

Post a Comment