Second Life of a Hungarian SharePoint Geek

March 13, 2012

Implementing a simple secure store provider

Filed under: BCS, Security, SP 2010 — Tags: , , — Peter Holpar @ 23:27

In my recent post I described two alternatives for accessing external data from the OWS process. One of the options – the RevertToSelf authentication – is not the best choice for a production environment. The other options – the Impersonate Windows Identity authentication mode – requires Secure Store Service (SSS) service, that is a SharePoint Server 2010 feature. It means you might have no ideal solution if you have a SharePoint Foundation 2010 environment.

Although the out-of-the-box Secure Store Implementation is the Microsoft.Office.SecureStoreService.Server.SecureStoreProvider class (Microsoft.Office.SecureStoreService assembly), that is really SharePoint Server only, the main interfaces located in the Microsoft.BusinessData.Infrastructure.SecureStore namespace(Microsoft.BusinessData assembly), that is available in SharePoint Foundation as well.

It means we can implement a custom Secure Store Implementation for SharePoint Foundation.

Below I show you a very basic sample for that. Of course, you can create more sophisticated versions based on the same concepts.

In Visual Studio 2010 we should create a new Class Library project. The target framework should be set to .NET 3.5 and platform target as x64. Since we have to deploy our assembly to GAC, we need a key file as well, to sign the strong-named assembly.

First I include the code for the helper classes.

The SimpleTargetApplicationDefinition class holds information about our target application.

  1. namespace SimpleSecureStoreProvider
  2. {
  3.     public class SimpleTargetApplicationDefinition : ITargetApplicationDefinition
  4.     {
  5.         public string ContactEmail { get; internal set; }
  6.  
  7.         public Uri CredentialManagementUrl { get; internal set; }
  8.  
  9.         public string FriendlyName { get; internal set; }
  10.  
  11.         public string Name { get; internal set; }
  12.  
  13.         public TargetApplicationType Type { get; internal set; }
  14.     }
  15. }

The SimpleTargetApplicationField describes a field of the target application.

  1. namespace SimpleSecureStoreProvider
  2. {
  3.     public class SimpleTargetApplicationField : ITargetApplicationField
  4.     {
  5.         public SecureStoreCredentialType CredentialType { get; internal set; }
  6.  
  7.         public bool IsMasked { get; internal set; }
  8.  
  9.         public string Name { get; internal set; }
  10.     }
  11. }

The SimpleSecureStoreCredential class contains a piece of the credential information.

  1. namespace SimpleSecureStoreProvider
  2. {
  3.     public class SimpleSecureStoreCredential : ISecureStoreCredential
  4.     {
  5.         bool disposed;
  6.  
  7.         public SecureString Credential { get; internal set; }
  8.  
  9.         public SecureStoreCredentialType CredentialType { get; internal set; }
  10.  
  11.         public void Dispose()
  12.         {
  13.             this.Dispose(true);
  14.         }
  15.  
  16.         private void Dispose(bool disposing)
  17.         {
  18.             if (!disposed)
  19.             {
  20.                 if (Credential != null)
  21.                 {
  22.                     Credential.Dispose();
  23.                 }
  24.                 disposed = true;
  25.             }
  26.         }
  27.  
  28.     }
  29. }

The main part of the code is the provider itself, that is SimpleSecureStoreProvider class. The skeleton of the class is like this:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections.ObjectModel;
  6. using Microsoft.BusinessData.Infrastructure.SecureStore;
  7. using System.Diagnostics;
  8. using System.Security;
  9.  
  10. namespace SimpleSecureStoreProvider
  11. {
  12.     public class SimpleSecureStoreProvider : ISecureStoreProviderExtended, ISecureStoreProvider
  13.     {
  14.     
  15.         …
  16.  
  17.     }
  18. }

We should return the application definition in the GetTargetApplication method. In this sample we return always the same data, regardless of the appId parameter.  As you can see, the sample is an example for a Group Target Application Type. In the GetTargetApplications method we return a single target application. I’ve included Trace commands to help us to monitor which methods are invoked in runtime.

  1. public ITargetApplicationDefinition GetTargetApplication(string appId)
  2. {
  3.     Trace.TraceInformation("GetTargetApplication {0} ({1})", appId, Process.GetCurrentProcess().Id);
  4.     return new SimpleTargetApplicationDefinition
  5.     {
  6.         ContactEmail = "user@company.com",
  7.         CredentialManagementUrl = null,
  8.         FriendlyName = "TargetApp",
  9.         Name = "AppDef",
  10.         Type = TargetApplicationType.Group
  11.     };
  12. }
  13.  
  14. public ReadOnlyCollection<ITargetApplicationDefinition> GetTargetApplications()
  15. {
  16.     Trace.TraceInformation("GetTargetApplications ({1})", Process.GetCurrentProcess().Id);
  17.     return new ReadOnlyCollection<ITargetApplicationDefinition>(new List<ITargetApplicationDefinition>() { GetTargetApplication("app") });
  18. }

In this application we have two fields, one for the user name and one for the password, as shown in the GetTargetApplicationFields method.

  1. public ReadOnlyCollection<ITargetApplicationField> GetTargetApplicationFields(string appId)
  2. {
  3.     Trace.TraceInformation("GetTargetApplicationFields {0} ({1})", appId, Process.GetCurrentProcess().Id);
  4.     List<ITargetApplicationField> fields = new List<ITargetApplicationField>();
  5.     fields.Add(new SimpleTargetApplicationField
  6.     {
  7.         CredentialType = SecureStoreCredentialType.WindowsUserName,
  8.         IsMasked = false,
  9.         Name = "UserName"
  10.     });
  11.     fields.Add(new SimpleTargetApplicationField
  12.     {
  13.         CredentialType = SecureStoreCredentialType.WindowsPassword,
  14.         IsMasked = false,
  15.         Name = "Password"
  16.     });
  17.  
  18.     return new ReadOnlyCollection<ITargetApplicationField>(fields);
  19. }

In the GetCredentials and GetRestrictedCredentials methods we should return credentials for the application. In this case I have a user name and password hardcoded, but these should be stored in a bit more secure and configurable way.

  1. public SecureStoreCredentialCollection GetCredentials(string appId)
  2. {
  3.     Trace.TraceInformation("GetCredentials {0} ({1})", appId, Process.GetCurrentProcess().Id);
  4.  
  5.     List<ISecureStoreCredential> creds = new List<ISecureStoreCredential>();
  6.  
  7.     creds.Add(new SimpleSecureStoreCredential
  8.     {
  9.         Credential = GetSecureString(@"domain\user"),
  10.         CredentialType = SecureStoreCredentialType.UserName
  11.     });
  12.     creds.Add(new SimpleSecureStoreCredential
  13.     {
  14.         Credential = GetSecureString("password"),
  15.         CredentialType = SecureStoreCredentialType.Password
  16.     });
  17.     return new SecureStoreCredentialCollection(creds);
  18. }
  19.  
  20. public SecureStoreCredentialCollection GetRestrictedCredentials(string appId)
  21. {
  22.     Trace.TraceInformation("GetRestrictedCredentials {0} ({1})", appId, Process.GetCurrentProcess().Id);
  23.  
  24.     return GetCredentials(appId);
  25. }

The GetSecureString method helps to convert a string into SecureString.

  1. private SecureString GetSecureString(string value)
  2. {
  3.     SecureString result = new SecureString();
  4.     if (value != null)
  5.     {
  6.         value.ToCharArray().ToList().ForEach(c => result.AppendChar(c));
  7.     }
  8.  
  9.     return result;
  10. }

The methods below are not implemented in this sample:

  1. public void DeleteCredentials(string appId)
  2. {
  3.     Trace.TraceInformation("DeleteCredentials {0} ({1})", appId, Process.GetCurrentProcess().Id);
  4.  
  5.     throw new NotImplementedException();
  6. }
  7.  
  8. public SecureStoreCredentialCollection GetCredentialsUsingTicket(string ticket, string appId)
  9. {
  10.     Trace.TraceInformation("GetCredentialsUsingTicket {0} ({1})", appId, Process.GetCurrentProcess().Id);
  11.  
  12.     throw new NotImplementedException();
  13. }
  14.  
  15. public string IssueTicket()
  16. {
  17.     Trace.TraceInformation("IssueTicket ({1})", Process.GetCurrentProcess().Id);
  18.  
  19.     throw new NotImplementedException();
  20. }
  21.  
  22. public ISecureStoreProviderInformation ProviderInformation
  23. {
  24.     get
  25.     {
  26.         Trace.TraceInformation("ProviderInformation ({1})", Process.GetCurrentProcess().Id);
  27.        
  28.         throw new NotImplementedException();
  29.     }
  30. }

After building the assembly and deploying it to the GAC, we should configure our BCS external system instance. Most important is to set the right Secure Store Implementation value (in my case it is SimpleSecureStoreProvider.SimpleSecureStoreProvider, simplesecurestoreprovider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9fc83bb193b5ea3b). The value of the Secure Store Target Application Id is irrelevant in this version.

image

If everything works as expected, we should be able to access the external data (in this case the Northwind database through an external list) using the credentials returned by our custom secure store implementation.

image

Advertisements

2 Comments »

  1. i am seeing all the properties for the Implemented classes as ReadOnly. How do you set the values when creating new objects?

    Comment by hari — June 21, 2012 @ 05:02

  2. Thank you for this valuable information. I am not c# developer. Can I have the entire code?
    thanks again.

    Comment by ed — August 29, 2012 @ 16:34


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: