Last week I reviewed some code that contains memory leaks and I noticed that they fetched some credentials from the Secure Store Service in a way that just didn't seem right to me. If you look at this code sample on MSDN you should be able to see what I mean.
They created a new SPServiceContext by first fetching a SPSite object of the Central Admin site. While this might (don't know) be required for the code sample, this doesn't make sense if we are inside a SharePoint context. I do know however why they thought they needed to go with this approach. They were creating a custom claims provider and the code for this custom claims provider does not run within a site context, but inside the Secure Token Service. They could however use a much easier approach to fetch the default secure store provider. Below you'll find the three classes I create for them that solved the memory issue.
class SecureStoreCredentials {
public string UserName { get; set; }
public string Password { get; set; }
public string Pin { get; set; }
}
static class SecureStringUtility {
public static string ConvertToString(this SecureString secureString) {
string retVal = null;
if (secureString != null) {
IntPtr pPlainText = IntPtr.Zero;
try {
pPlainText = Marshal.SecureStringToBSTR(secureString);
retVal = Marshal.PtrToStringBSTR(pPlainText);
} finally {
if (pPlainText != IntPtr.Zero) {
Marshal.FreeBSTR(pPlainText);
}
}
}
return retVal;
}
}
class SecureStoreUtility {
public static SecureStoreCredentials GetCredentials(string applicationId) {
if (string.IsNullOrEmpty(applicationId)) {
throw new ArgumentNullException("applicationId");
}
var retVal = new SecureStoreCredentials();
SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);
SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
ISecureStore iss = ssp.GetSecureStore(context);
SecureStoreCredentialCollection credentials = null;
try {
credentials = iss.GetCredentials(applicationId);
if (credentials != null) {
foreach (SecureStoreCredential credential in credentials) {
if (credential == null) {
continue;
}
switch (credential.CredentialType) {
case SecureStoreCredentialType.WindowsUserName:
case SecureStoreCredentialType.UserName:
retVal.UserName = credential.Credential.ConvertToString();
break;
case SecureStoreCredentialType.WindowsPassword:
case SecureStoreCredentialType.Password:
retVal.Password = credential.Credential.ConvertToString();
break;
case SecureStoreCredentialType.Pin:
retVal.Pin = credential.Credential.ConvertToString();
break;
}
}
}
} catch (Exception ex) {
Debug.WriteLine(ex.ToString());
} finally {
if (credentials != null) {
credentials.Dispose();
}
}
return retVal;
}
}
The important line here is:
SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);
Which fetches a SPServiceContext without the need of a SPSite object.
Have fun with it!
Wesley