How do I leverage Secure Store from within my .Net Connectivity Assembly?
With Database, Web Service and WCF connectors, there is one and only one external system. BCS understands how to connect to these external systems, and it supports Secure Store integration out-of-box for these connectors. Within a .NET assembly connector, the .NET assembly code can use zero, one or many external systems. Since BCS does not know what the .NET assembly code does, it does not support Secure Store integration out of box for the .NET assembly connector.
Even though BCS does not have OOB support for Secure Store in .NET assembly connector, it provides two different mechanisms to leverage Secure Store within a .NET connectivity assembly. In the first mechanism (no-code approach) BCS does the heavy lifting of reading the credentials from secure store whereas in the second mechanism (code approach) .NET connectivity assembly needs to do the heavy lifting.
This blog explains how both approaches are used.
No-code approach
As the name suggests, your .NET connectivity assembly does not directly interact with Secure Store. Using filters, it can utilize BCS to read the credentials from Secure Store and get the credentials as input parameters to the method. This approach is not unique to .NET assembly connector and can be used with Database, Web Service and WCF connectors.
When writing the method in .NET connectivity assembly, you will need to have input parameters for credentials. The following example shows “GetEntity” method with input parameters for username and password along with other parameters.
public SampleEntity GetEntity(string username, string password, int id){
// parameters username and password
// contains the credentials
}
To get the credentials in the username and password parameters, you will need to use filters UsernameFilter and PasswordFilter. In the metadata model, you will need to define the properties “SecondarySsoApplicationId” and “SsoProviderImplementation” for LobSystemInstance and associate the filters to appropriate input parameter of the method. When BCS encounters filters, it uses the properties “SecondarySsoApplicationId” and “SsoProviderImplementation” for secure store target application id and secure store provider.
<LobSystemInstance ...>
<Properties>
<!-- other properties omitted –>
<Property Name="SecondarySsoApplicationId"
Type="System.String">MyLobAppId</Property>
<Property Name="SsoProviderImplementation" Type="System.String">
Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Property>
</Properties>
</LobSystemInstance>
Now let’s define the two filters for the method.
<Method Name="GetEntity">
<FilterDescriptors>
<FilterDescriptor Type="Username" Name="UserNameFilter"/>
<FilterDescriptor Type="Password" Name="PasswordFilter"/>
</FilterDescriptors>
</Method>
With the filters defined for the method, the following snippet shows how to associate the two filters to the input parameters of the method.
<Parameter Direction="In" Name="username">
<TypeDescriptor TypeName="System.String" Name="username"
AssociatedFilter="UserNameFilter"/>
</Parameter>
<Parameter Direction="In" Name="password">
<TypeDescriptor TypeName="System.String" Name="password"
AssociatedFilter="PasswordFilter"/>
</Parameter>
That’s all you will need to leverage secure store in .NET connectivity assembly. When BCS executes the method, it will read the credentials from the secure store and set the credentials in the input parameters of the method.
Code-approach
Before we start writing code for leveraging Secure Store, let’s take a look at how BCS leverages Secure Store in other connectors. Other connectors in BCS rely on the metadata model to provide them information about Secure Store and the target application id where the credentials can be retrieved from for authenticating with the external system. For example, in the case of the Database connector, the properties for LobSystemInstance in the metadata model look like this:
<LobSystemInstance ...>
<Properties>
<!-- other properties omitted –>
<Property Name="SsoApplicationId"
Type="System.String">MyLobAppId</Property>
<Property Name="SsoProviderImplementation" Type="System.String"> Microsoft.Office.SecureStoreService.Server.SecureStoreProvider, Microsoft.Office.SecureStoreService, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Property>
</Properties>
</LobSystemInstance>
Reading the metadata model, the Database connector understands that it needs credentials to connect to the database and the credentials can be obtained from the specified secure store provider. Once the database connector has the necessary information for the Secure Store provider, it reads the credentials from the provider and impersonates the user for database connection.
The following sections explain how to read the metadata model properties within your .NET assembly code and use secure store APIs to read user credentials.
Reading metadata model properties
To read metadata model properties in the .NET connectivity assembly, code in .NET assembly must implement IContextProperty interface. The following code shows a sample implementation of IContextProperty interface.
Sample implementation of IContextProperty
using IContextProperty = Microsoft.BusinessData.SystemSpecific.IContextProperty;
public class SampleConnector : IContextProperty
{
private IMethodInstance methodInstance;
private ILobSystemInstance lobSystemInstance;
private IExecutionContext executionContext;
#region IContextProperty implementation
public IMethodInstance MethodInstance
{
get { return this.methodInstance; }
set { this.methodInstance = value; }
}
public ILobSystemInstance LobSystemInstance
{
get { return this.lobSystemInstance; }
set { this.lobSystemInstance = value; }
}
public IExecutionContext ExecutionContext
{
get { return this.executionContext; }
set { this.executionContext = value; }
}
#endregion
// unrelated code removed for brevity
}
When the .NET connectivity assembly class implements the IContextProperty interface, the BCS runtime automatically initializes IContextProperty properties.
At runtime, the code has the context (method instance and LOB system instance) in which it executes; a method implemented in the code can read the properties from the metadata model. The following code shows how LobSystemInstance properties are read:
public SampleEntity GetEntity(int id)
{
// read the LobSystemInstance property defined in metadata model
string provider =
LobSystemInstance.GetProperties()[“ssoProviderImplementation”]
as string;
// unrelated code removed for brevity
}
Interacting with Secure Store
When the .NET connectivity assembly requires reading one credential from Secure Store, we recommend using the following property names.
ssoProviderImplementation – Secure Store provider
SecondarySsoApplicationId – Target Application for Secure Store
ssoProviderImplementation and SecondarySsoApplicationId properties are supported for modeling .NET assembly connector models in SPD.
If the .NET connectivity assembly requires reading more than one credential from Secure Store, choose a property name that makes the most sense for your scenario.
Sample implementation for reading credentials
The first step is to instantiate the Secure Store provider from the provider implementation. In your code, make sure that the provider implements ISecureStoreProvider interface.
A simple implementation for instantiating the provider may look like
private ISecureStoreProvider GetSecureStoreProvider()
{
// error checking removed for brevity
string provider = this.LobSystemInstance.GetProperties()
[“ssoProviderImplementation”] as string;
Type providerType = Type.GetType(provider);
return Activator.CreateInstance(providerType)
as ISecureStoreProvider;
}
Once the Secure Store provider has been instantiated, it’s time to get the credentials from Secure Store. Secure Store returns a SecureStoreCredentialCollection for the user and target id, which contains a collection of credentials. ISecureStoreSecureCredentials interface defines the credentials type and its value.
private void ReadCredentialsFromSecureStore()
{
// error checking removed for brevity
string targetId =
LobSystemInstance.GetProperties()[“SecondarySsoApplicationId”]
as string;
ISecureStoreProvider provider = GetSecureStoreProvider();
// get the credentials for the user on whose behalf the code
// is executing
using(SecureStoreCredentialCollection credentials =
provider.GetRestrictedCredentials(targetId))
{
SecureString secureUsername;
SecureString securePassword;
// look for username and password in credentials
foreach (ISecureStoreCredential credential in credentials)
{
switch (credential.CredentialType)
{
case SecureStoreCredentialType.UserName:
case SecureStoreCredentialType.WindowsUserName:
secureUsername = credential.Credential;
break;
case SecureStoreCredentialType.Password:
case SecureStoreCredentialType.WindowsPassword:
securePassword = credential.Credential;
break;
default:
break;
}
}
// username and password have been read
// use them as necessary.
}
//NOTE: Since we are getting the credentials in the using block,
//all the credentials that we get will be disposed after the
//using block. If you need to cache the credentials don't use
//using block but dispose the credentials when you are done
//with it.
}
The above code shows how the credentials are read from secure store. These credentials can be used for authentication to an external system.
Summary
Depending upon your scenario, you may choose no-code approach or code approach to leverage secure store in your .NET connectivity assembly. The following table lists the differences between these two approaches.
No-code approach |
Code approach |
|
Interaction with secure store API required? |
No |
Yes |
Exclusive to .NET assembly connector |
No |
Yes |
SPD support |
Yes |
Partially |
Impacts method signature |
Yes |
No |
Read from multiple Secure Store |
No |
Yes |
Can credentials be set at LobSystemInstance level? |
No |
Yes |
-Kaushik Raj