Almost all the blog post on BCS with Secure Store Service in SharePoint 2010 provides examples with Share Point Designer. What if ,this has to be done in Visual Studio ? The result of this question is the birth of this blog post. In this post you can find how a BCS object which requires an authentication can be accessed through an .net connector assembly designed in Visual Studio.
This post does not deal with any usual stuff like how to connect SQL sever or how to connect a web service etc.. But it will provide you the necessary information that are required to access the Secure Store Credentials in BCS. If your external data requires an authentication and those authentication details are in Secure Store Service then this post is for you. Now let’s see how the credentials can be retrieved through .net code.
Before executing this solution ensure that you have configured Secure Store Service and have added a secure store target application. If you are not sure on how this has to be done, you can refer this technet link
Find below the steps that I have followed to create a new Secure Store Target Application
The field type can be configured in the below screen. For this demo I have selected user name and password and changed the description by adding the word demo.
In the members section I have added the administrator just for the sake of demo , instead you can provide a AD User group which would be the best approach in terms of User Management.
Once necessary details are provided , the newly created Target Application will be displayed in the list of available Target Applications.
To add credentials for the newly created Target Application, click the item and select Set Credentials.
I have given “TestAccount” as user name and “P@ssw0rd” as password. The objective is to read this credentials through code in the BCS model what we are going to create . Depending on the business need and permission available for the credential this approach can be applied for any purpose like accessing a Web Service or constructing a connection string or accessing a file share etc.. .
Open up the Visual Studio. Create a new empty SharePoint project and add a new Business Data Connectivity Model SPI. I have named my model as “BcsWithSSS” and renamed the feature to “BcsAndSecureStore” .
To map the Secure Store Target Application ID to the new Model , double click the model and select the properties of LobSystemInstances of model and click the Custom Properties
Add the below items one by one and close the window.
Name | Type | Value |
Provider | System.String | Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c |
ApplicationId | System.String | BcsDemoForBlog |
Create a class named as SecureStoreManager and place the below code. This code extracts the user name and password based on the provider name and application id specified in the properties of LobSystemInstance. This piece of code requires a reference to Microsoft.BusinessData.dll which is deployed in GAC and can be located under the path C:\WINDOWS\assembly\GAC_MSIL\Microsoft.BusinessData\14.0.0.0__71e9bce111e9429c
To physically locate assemblies deployed to GAC, refer this link.
public class SecureStoreManager
{
private SecureStoreManager()
{
}
public string UserName { get; set; }
public string PassWord { get; set; }
public SecureStoreManager(ILobSystemInstance LobSysInstance)
{
string Provider = LobSysInstance.GetProperties()["Provider"] as string;
if (Provider == null)
{
throw new LobBusinessErrorException("Unable to fetch the provider info.");
}
Type TypeOfProvider = Type.GetType(Provider);
ISecureStoreProvider ProviderInstance = (ISecureStoreProvider)Activator.CreateInstance(TypeOfProvider);
string TargetName = LobSysInstance.GetProperties()["ApplicationId"] as string;
SecureStoreCredentialCollection credentials = ProviderInstance.GetCredentials(TargetName);
foreach (ISecureStoreCredential cred in credentials)
{
if (cred.CredentialType == SecureStoreCredentialType.UserName)
{
UserName = DecryptString(cred.Credential);
}
else if (cred.CredentialType == SecureStoreCredentialType.Password)
{
PassWord = DecryptString(cred.Credential);
}
}
}
private string DecryptString(System.Security.SecureString CrString)
{
string str = null;
IntPtr pr = IntPtr.Zero;
try
{
pr = Marshal.SecureStringToBSTR(CrString);
str = Marshal.PtrToStringBSTR(pr);
}
finally
{
Marshal.FreeBSTR(pr);
}
return str;
}
}
Reference to below namespaces are required for the above code to compile properly.
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.Runtime;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using System.Runtime.InteropServices;
The Entity Service class has to be updated to implement the interface IContextProperty. Which in turn requires the reference to below mentioned namespaces.
using Microsoft.BusinessData.SystemSpecific;
using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.Runtime;
This interface IContextProperty is required to fetch the custom properties that we have added to our model. During runtime “LobSystemInstance” object will be populated with the instance specific properties.
The below approach can be used to retrieve any custom properties that are added to model.
string MyCustomPropertyValue= LobSysInstance.GetProperties()["MyCustomProperty"] as string;
The below code shows how the interface is implemented in Entity1Service class
private static IMethodInstance methodInstance;
private static ILobSystemInstance lobSystemInstance;
private static IExecutionContext executionContext;
public IMethodInstance MethodInstance
{
get { return methodInstance; }
set { methodInstance = value; }
}
public ILobSystemInstance LobSystemInstance
{
get { return lobSystemInstance; }
set { lobSystemInstance = value; }
}
public IExecutionContext ExecutionContext
{
get { return executionContext; }
set { executionContext = value; }
}
To fetch the Secure Store data, I have created an instance of the class “SecureStoreManager” in the default ReadList method. In this sample ,user name and password are not used for any specific purpose. It’s been called in this method to ensure that our code to fetch Secure Store Data is working perfectly.
public static IEnumerable<Entity1> ReadList()
{
SecureStoreManager sso = new SecureStoreManager(lobSystemInstance);
//TODO: This is just a sample. Replace this simple sample with valid code.
Entity1[] entityList = new Entity1[1];
Entity1 entity1 = new Entity1();
entity1.Identifier1 = "0";
entity1.Message = "User Name : " + sso.UserName + " Password : " + sso.PassWord + " from Secure Store Service :-) .This is just a Demo. Use this credentials to call a WebService or SQL Server...";
entityList[0] = entity1;
return entityList;
}
Add a property in the feature which deploys this model with key “SiteUrl” (Case Sensitive) with value set to URL of any of the Web Application in the farm. Although this property is not used anywhere in our code, this is required to bypass an bug in Share Point. There are some work around available (Refer Link). But this is the easiest approach. The only downside is this approach is, the URL has to be changed and repackaged for change in environment, like dev to QA etc..
Deploy the code and create a new External List (Refer Link) based out of our model. The below is the final out put you can expect if you have configured every thing properly.
Leave a comment