Wednesday 30 October 2013

A Quick Guide to Getting, Setting and Copying User Profile Properties using PowerShell

Whether you're a SharePoint Administrator or SharePoint Developer, being able to quickly read, update or copy User Profile properties is a handy skill to have. Using PowerShell to get and set User Profile properties is both quick and easy. This post outlines how to do it!

Getting the User Profile


The basic PowerShell code for getting a user profile, using a users UPN (User Principal Name):
[void][reflection.assembly]::Loadwithpartialname("Microsoft.Office.Server") | out-null;            
$site=new-object Microsoft.SharePoint.SPSite("https://c05470sp10:7443");            
$serviceContext = Get-SPServiceContext $site;            
$site.Dispose();            
$upm = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext);            
$userProfile = $upm.GetUserProfile("myarlett@company.com");



The basic PowerShell code for getting a user profile, using the user's login name:
[void][reflection.assembly]::Loadwithpartialname("Microsoft.Office.Server") | out-null;            
$site=new-object Microsoft.SharePoint.SPSite("https://c05470sp10:7443");            
$serviceContext = Get-SPServiceContext $site;            
$site.Dispose();            
$upm = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext);
$userProfile = $upm.GetUserProfile("company\myarlett");


Listing all the Profile Fields (Properties) and their Types


List the user profile properties (including the field type). This is handy, because we'll need to know what the field type is before trying to set it's value:
$userProfile.Properties | sort DisplayName | FT DisplayName,Name,@{Label="Type";Expression={$_.CoreProperty.Type}}



Getting the Value of a Property

Get the users About Me property (HTML):
$userProfile["AboutMe"].Value


Setting the Values of Properties


Update the users Location (String field):
$userProfile["SPS-Location"].Value = "London";            
$userProfile.Commit();

Update the users Manager (Person field):
$userProfile["Manager"].Value = (Get-SPWeb https://c05470sp10:7443).EnsureUser("company\fred");
$userProfile.Commit();

Note that in the above example, we have retrieved an SPUser object (for the manager) from the Central Admin site, using the EnsureUser method.

Copying User Profile Properties between Profiles


Copy fields from one user profile to another:
$userProfile2 = $upm.GetUserProfile("company\matthewette");            
$userProfile2["AboutMe"].Value = $userProfile["AboutMe"];            
$userProfile2.Commit();

See Also




Monday 21 October 2013

Using the SharePoint Secure Store Application for Database Connection Settings

Introduction

A few weeks back I blogged about accessing the SharePoint Secure Store using C# to retrieve user credentials. Credentials in the Secure Store are stored securely and can be managed via the Central Administration site. That post is here: Retrieving Credentials from the SharePoint Secure Store using C#

In this post, I want to expand on that concept, and demonstrate how the SharePoint Secure Store can be used for storing database connection settings (username, password, database, server).

Often SharePoint solutions are required to access external databases. The SharePoint Secure Store solves the problem of securely storing and managing credentials, but what about managing the database server and database name?

The example below extends the class designed in the first post, to demonstrate how credential and database information can be retrieved by code to connect to and authenticate with external database systems, such as Microsoft SQL Server.

This article assumes you have the SharePoint Secure Store Application configured. To complete the example, you will need access to the Central Administration site, and will need permissions to create new Target Applications and deploy solutions.

The source code for this project can be downloaded from the Microsoft TechNet Gallery, here: Retrieving Credentials from the SharePoint Secure Store using C#

Creating a Target Application in the Secure Store

Before looking at the code, we are going to step through creating a Target Application in the Secure Store.

The new Target Application we create will store a generic user credential (username and password), a database name, and a database server name. We will then use this information in the code example to connect and authenticate with a SQL Server used to store HR information.. The name of the target application will be a description of the target SQL server and database we are connecting with, in this example, HRPro.

1. Browse to the Central Administration site
2. Click on Application Management
3. Click on Manage Service Applications
4. Click the Secure Store Application
5. Create a new Target Application
5.1 On the ribbon, in the Manage Target Applications, click New


5.2 In the Create New Secure Store Target Application page, enter the following information;

Target Application ID: HrPro
Display Name: HR Pro
Contact E-mail: (enter your email address)
Target Application Type: Group



5.3 Click Next. On the next page, Specify the credential fields for the Secure Store Target Application, configure the fields that are used to store the credential and database information.

Special attention needs to be paid here, as a standard for all applications needs to be set. In this example, I'm specifying that the SqlServer must use the field type Key, and the database must use the field type Generic. The code that is used to access this information doesn't have access to field names, only the FieldType enumeration. The standard is: [FieldType]Key = the SQL Server, and [FieldType]Generic = the Database name. This will become clearer in the code example.

Configure the following fields:

Field NameField TypeMasked
UserNameUsernameNo
PasswordPasswordYes
SqlServerKeyNo
DatabaseGenericNo




5.4 Click Next. On the next page, Specify the membership Settings, you need to enter administrators and members. Administrators are people who will be able to manage this target application, while members are people who will have permissions to retrieve (read) the user credentials.

Since the user credentials and database information will be accessed via code under the context of a standard site user, we are adding Domain Users as Members. The SharePoint farm account (and any other administrators) should be added as administrators.


5.5 Click OK to save the new Target Application.
6. Set the credentials of the new Target Application
6.1 Select the new Target Application, and click Set (in the Credentials section of the ribbon)



6.2 Enter the username, password, SQL Server and database information. The user and password need to be a SQL Server user (unless you plan on using impersonation to open the connection to SQL - which is a blog for another day).



6.3 Click OK to save the credential information

You have finished creating the new Target Application. The next step is to write some code that will access and use the credentials and database settings.

Building a Class to Access the Credentials


This part of the example requires creating a some classes and methods for accessing the secure store, retrieving a credential object, and returning it to the caller.

1. Create a new empty SharePoint Project (deploy as a farm solution)
2. Add the following references to the project:

Microsoft.Office.SecureStore.dll (see the reference below about finding the Microsoft.Office.SecureStore.dll in the GAC (Global Assembly Cache))
Microsoft.BusinessData.dll

3. Add a new class to the project, called SecureStoreProxy
4. Make the class as public and static

namespace SecureStoreCredentialsExample
{
 public static class SecureStoreProxy
 {
 
 }
}


5. Add the following using statements

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using Microsoft.Office.SecureStoreService.Server;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;


4. Add the following code to the SecureStoreProxy class
4.1. Add CredentialType enum. This will be used by the GetDatabaseConnectionSettingsFromSecureStoreService method.

public enum CredentialType
{
 Domain,
 Generic
}


4.2. Add a new class, BaseCredentials, to store credential information. The base class implements IDisposable to ensure the classes SecureString properties are correctly disposed of. It also contains a method for returning a SecureString as a String.

Add an additional two classes, that inherit BaseCredentials, for storing Windows credentials, and Database credentials.

The class that stores the Windows credentials contains an extra property to hold the domain name, and an updated constructor (this class isn't used in this example, but is included for completeness).

The class that stores the database credentials contains extra properties to hold the Sql Server and Database values, a method to create a default connection string, and an updated constructor.

public class BaseCredentials : IDisposable
{
 private readonly SecureString _userName;
 public String UserName
 {
  get { return ConvertToUnsecuredString(_userName); }
 }
 private readonly SecureString _password;
 public String Password
 {
  get { return ConvertToUnsecuredString(_password); }
 }
 public BaseCredentials(SecureString username, SecureString password)
 {
  _userName = username.Copy();
  _password = password.Copy();
 }
 protected string ConvertToUnsecuredString(SecureString securedString)
 {
  if (securedString == null) return String.Empty;
  IntPtr uString = IntPtr.Zero;
  try
  {
   uString = Marshal.SecureStringToGlobalAllocUnicode(securedString);
   return Marshal.PtrToStringUni(uString);
  }
  finally
  {
   Marshal.ZeroFreeGlobalAllocUnicode(uString);
  }
 }

 private Boolean _isDisposed;
 public void Dispose()
 {
  if (_isDisposed) return;
  _userName.Dispose();
  _password.Dispose();
  _isDisposed = true;
 }
}

public class UserCredentials : BaseCredentials
{
 public String DomainName;
 public UserCredentials(SecureString username, SecureString password, SecureString domainName) : base(username, password)
 {
  DomainName = base.ConvertToUnsecuredString(domainName);
 }
 public UserCredentials(SecureString username, SecureString password): base(username, password)
 {
 }
}

public class DatabaseCredentials : BaseCredentials
{
 public readonly String Database;
 public readonly String SqlServer;
 public readonly Boolean UseWindowsAuthentication;
 public String DefaultSqlConnectionString
 {
  get
  {
   var connectionString = new SqlConnectionStringBuilder() { DataSource = SqlServer, InitialCatalog = Database};
   if (UseWindowsAuthentication)
   {
    connectionString.IntegratedSecurity = true;
   }
   else
   {
    connectionString.UserID = UserName;
    connectionString.Password = Password;
   }
   return connectionString.ToString();
  }
 }
 
 public DatabaseCredentials(SecureString username, SecureString password, SecureString sqlServer, SecureString database, Boolean useWindowsAuthentication) : base(username, password)
 {
  Database = ConvertToUnsecuredString(database);
  SqlServer = ConvertToUnsecuredString(sqlServer);
  UseWindowsAuthentication = useWindowsAuthentication;
 }
}



4.3. Add a new public static method used to retrieve database credential information from the Secure Store. This method takes an Application ID (a target application id), and the CredentialType enumeration as inputs, and returns a DatabaseCredentials object.

public static DatabaseCredentials GetDatabaseConnectionSettingsFromSecureStoreService(string applicationId, CredentialType credentialType)
{
 ISecureStoreProvider provider = SecureStoreProviderFactory.Create();
 if (provider == null)
 {
  throw new InvalidOperationException("Unable to get an ISecureStoreProvider");
 }
 using (SecureStoreCredentialCollection credentials = provider.GetCredentials(applicationId))
 {
  var un = from c in credentials
     where c.CredentialType == (credentialType == CredentialType.Domain ? SecureStoreCredentialType.WindowsUserName : SecureStoreCredentialType.UserName)
     select c.Credential;

  var pd = from c in credentials
     where c.CredentialType == (credentialType == CredentialType.Domain ? SecureStoreCredentialType.WindowsPassword : SecureStoreCredentialType.Password)
     select c.Credential;

  var s = from c in credentials
     where c.CredentialType == SecureStoreCredentialType.Key
     select c.Credential;

  var db = from c in credentials
    where c.CredentialType == SecureStoreCredentialType.Generic
    select c.Credential;

  SecureString userName = un.First(d => d.Length > 0);
  SecureString password = pd.First(d => d.Length > 0);
  SecureString sqlServer = s.First(d => d.Length > 0);
  SecureString database = db.First(d => d.Length > 0);
  var databaseConnectionSettings = new DatabaseCredentials(userName, password, sqlServer, database, credentialType == CredentialType.Domain);
  return databaseConnectionSettings;
 }
}



The method connects to the Secure Store, and retrieves the credentials of the Target Application. If the user context that the code is running isn't in the membership of the Target Application, an exception will be thrown.

Once the method has connected to the Secure Store and retrieved the Target Application's credentials (returned as a SecureStoreCredentialCollection), it parses the collection, extracting the username, password, SQL Server and database into a new DatabaseCredentials object.

From this method, you can see the importance of deciding on a standard for which FieldType contains the SQL server, and which FieldType contains the database. There is no method to get the name of a Credential from the provide. The only values that get returned are the CredentialType (Generic, Pin, Key, User, Password, Windows User, Windows Password), and the StringString value itself.

5. Build the solution.


Building a Webpart that uses the Credentials to Connect to a SQL Server Database


The final step in our example is to build a webpart the retrieves the information about the current user from the HR SQL Server database, augments it with data from the SharePoint User Profile Service, and displays it to the user.

1. Add a new standard webpart to the project called GetInformationFromSql
2. Add a the following using statements to the webparts code file

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;


3. Copy the following code into the webpart file.

The webpart contains a label and some code that runs when OnPreRender event. During the OnPreRender event, the webpart will retrieve information about the currently logged on user from the HR SQL server using the credentials retrieved from the Secure Store. It will augment this information with additional information from the User Profile Application.

Notice that the call to GetDatabaseSettingsFromSecureStore is within a Using block to ensure the object is disposed of.

namespace SecureStoreCredentialsExample.GetInformationFromSql
{
 [ToolboxItemAttribute(false)]
 public class GetInformationFromSql : WebPart
 {
  private Label _results;
  private const String SqlCommandText = "select t1.ID,t1.Full_Name, t1.Initials, t1.Job_Title, t1.Ince_Reference, t1.Location, t1.Start_Date, t1.department from HRPro.dbo.personnel_Records t1 where t1.leaving_date is null and t1.phi = 0 and ince_reference = '{0}'";

  private String _applicationId = "HrPro";
  [WebBrowsable(true), WebDisplayName("Application Id"), WebDescription("Secure Store Target Application ID"),
   Personalizable(PersonalizationScope.Shared), Category("Webpart Settings")]
  public String ApplicationId
  {
   get { return _applicationId; }
   set { _applicationId = value; }
  }


  protected override void CreateChildControls()
  {
   _results = new Label();
   Controls.Add(_results);
  }

  protected override void OnLoad(EventArgs e)
  {
   base.OnLoad(e);
   if (!Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), "ssce"))
   {
    Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "ssce", "/_layouts/ssce.js?v1");
   }
  }

  protected override void OnPreRender(EventArgs e)
  {
   base.OnPreRender(e);
   _results.Text = String.Empty;
   try
   {
    //Get Database settings from the Secure Store
    using (var databaseSettings = SecureStoreProxy.GetDatabaseConnectionSettingsFromSecureStoreService(ApplicationId,SecureStoreProxy.CredentialType.Generic))
    {
     using (var connection = new SqlConnection(databaseSettings.DefaultSqlConnectionString))
     {
      var userId = SPContext.Current.Web.CurrentUser.LoginName.Contains(@"\")
       ? SPContext.Current.Web.CurrentUser.LoginName.Substring(SPContext.Current.Web.CurrentUser.LoginName.IndexOf(@"\", StringComparison.InvariantCultureIgnoreCase) + 1)
       : SPContext.Current.Web.CurrentUser.LoginName;
      var sqlCommand = new SqlCommand(String.Format(SqlCommandText, userId), connection) { CommandType = CommandType.Text };
      connection.Open();
      var reader = sqlCommand.ExecuteReader();
      if (reader.HasRows)
      {
       reader.Read();
       String fullname = reader["Full_Name"] == DBNull.Value ? String.Empty : (String)reader["Full_Name"];
       String initials = reader["Initials"] == DBNull.Value ? String.Empty : String.Format("({0})", reader["Initials"]);
       String inceRef = reader["Ince_Reference"] == DBNull.Value ? String.Empty : (String)reader["Ince_Reference"];
       String location = reader["Location"] == DBNull.Value ? String.Empty : String.Format("Office: {0}", reader["Location"]);
       String jobTitle = reader["Job_Title"] == DBNull.Value ? String.Empty : (String)reader["Job_Title"];
       String department = reader["Department"] == DBNull.Value ? String.Empty : String.Format("({0})", reader["Department"]);
       DateTime startdate = reader["Start_Date"] == null ? DateTime.MinValue : (DateTime)reader["Start_Date"];
       Uri imageUrl;
       String aboutMe;
       GetProfileInformation(inceRef, out imageUrl, out aboutMe);
       var sb = new StringBuilder();
       aboutMe = aboutMe == String.Empty ? String.Empty : String.Format("<div><span onclick=\"javascript:displayElementInPopup('{0}', 'About Me')\">About Me</span><div><div id=\"{0}\">{1}</div></div></div>", String.Format("incePInstance{0}", inceRef.Trim()), aboutMe);
       String photoWrapper = String.Format("<div><img src=\"{0}\" alt=\"{1}\" style=\"max-width:48px;\"/></div>", imageUrl, fullname);
       String infoWrapper = String.Format("<div><div>{0} {1}</div><div>{2} {3}</div><div>{4}</div><div>{5}</div></div>", fullname, initials, jobTitle, department, location, aboutMe);
       sb.Append(String.Format("<div><table><tr><td>{0}</td><td>{1}</td></tr></table></div>", photoWrapper, infoWrapper));
       _results.Text = sb.ToString();
      }
      connection.Close();
     }
    }
    
   }
   catch (Exception exception)
   {
    _results.Text = String.Format("Something went wrong accessing information from the HR system. Error: {0}",exception.Message);
   }
  }

  private static void GetProfileInformation(string userName, out Uri imageUrl, out string aboutMe)
  {
   try
   {
    SPServiceContext serviceContext = SPServiceContext.GetContext(GetCentralAdministrationSite());
    var upm = new UserProfileManager(serviceContext);
    var domain = Environment.UserDomainName;
    String samAccount = String.Format("{0}\\{1}", domain, userName);
    if (!upm.UserExists(samAccount))
    {
     aboutMe = String.Empty;
     imageUrl = new Uri("/_layouts/images/person.gif", UriKind.Relative);
     return;
    }
    UserProfile up = upm.GetUserProfile(samAccount);
    UserProfileValueCollection pictureUrl = up["PictureURL"];
    UserProfileValueCollection aboutMeField = up["AboutMe"];
    aboutMe = aboutMeField.Value == null ? String.Empty : aboutMeField.Value.ToString();
    imageUrl = new Uri(pictureUrl.Value != null ? pictureUrl.Value.ToString() : "/_layouts/images/person.gif", UriKind.RelativeOrAbsolute);
   }
   catch (Exception)
   {
    aboutMe = String.Empty;
    imageUrl = new Uri("/_layouts/images/ince/anon.png", UriKind.Relative);
   }
  }

  private static SPSite GetCentralAdministrationSite()
  {
   var webApplication = SPAdministrationWebApplication.Local;
   if (webApplication == null)
   {
    throw new NullReferenceException("Unable to get the Central Administration Site.");
   }
   var caWebUrl = webApplication.GetResponseUri(SPUrlZone.Default);
   if (caWebUrl == null)
   {
    throw new NullReferenceException("Unable to get the Central Administration Site. Could get the URL of the Default Zone.");
   }
   return webApplication.Sites[caWebUrl.AbsoluteUri];
  }
 }
 
}


4. Build and deploy the project
5. Add the webpart to a page
6. The webpart connects to the HR System (using the credentials, SQL Server and database information provided from the Secure Store) and displays information.




See Also

Retrieving Credentials from the SharePoint Secure Store using C#

References

How to use the DirectoryServices Namespace in ASP (Double-Hop Authentication Issue)
Microsoft.Office.SecureStoreService.dll
Getting credients from the Secure Store Provider
Visual Studio Project Sample: Retrieving Credentials from the SharePoint Secure Store using C#

Wednesday 9 October 2013

Customising the SharePoint Advanced Search Page

The SharePoint Enterprise Search site contains a number of pages when it's created. One of those pages is an Advanced Search page that can be used to create advanced search queries.




I answered a question in a forum recently about a specific problem customising this page. The forum user wanted to set the Result Type to "All Results", hide the Result Type label and drop-down box, but keep the property selector in the "Add property restrictions..." section. You can use the Advanced Search Box webparts toolpane properties to remove the Results Type selector, however, it also removes the "Add property restrictions..." section.

There are two solutions to achieve this; use JavaScript, or use CSS. The answer I posed was using CSS with specific selectors to hide the elements used to display the "Result Type" label and drop-down box.

Solution

1. Edit the Advanced Search page
2. Edit the Advanced Search Box webpart
3. In the webparts toolbox, expand the Scopes section
4. Un-check Show the languages picker
5. In the webparts toolbox, expand the Properties section


6. Copy the text in the Properties textbox to a notepad editor
7. Search for the string "<ResultTypes>"
8. Remove all the <ResultType> elements, except for the "All Results" element.


9. Copy the XML back into the Properties textbox in the webparts toolbox pane
10. Apply the changes to the Advanced Search Box webpart.
11. Add an HTML Form webpart to the page.
12. Add the following markup to the HTML form webpart

<style type="text/css">
td.ms-advsrchText-v2 > select[title='Result Type']{display:none}
td.ms-advsrchText-v1 > label[for*='_ASB_SS_rtlb']{display:none}
</style>


13. Save the HTML Form webparts properties
14. Save the page page.

The Advanced Search page now includes the property restrictions for the All Results result type, without displaying the Result Type picker.



CSS Explanation

To remove the Result Type label and drop-down box, we need two CSS rules to hide those elements.

The first CSS rule, "td.ms-advsrchText-v2 > select[title='Result Type']{display:none}", applies display:none to all select elements that have a title equal to "Result Type", appearing directly after a <td> element that contains the ms-advsrchText-v2 class (<td class="ms-advsrchText-v2>)

The second rule is slightly trickier. It looks as though there isn't something specific enough to hide the "Result Type" label with a CSS rule (without hiding other rows). However, the "for" attribute of the label contains a randomly generated unique string. This string, is only partially random. The last part of the string is constant, and we can use an attribute selector to select all labels that have a "for" element that ends in "_ABS_SS_rtlb". This does the job perfectly, selecting only the "Results Type" label.

This can be seen using the Internet Explorer Developer Tools (F12 in your Internet Explorer browser).




References

Forum content (advanced-search-web-part)


Saturday 5 October 2013

Retrieving Credentials from the SharePoint Secure Store using C#

Introduction

There are times when you need to connect SharePoint to an system external. If the external system requires authentication, unless you have Kerberos authentication (and delegation) configured in your environment, you will suffer from the "Double Hop" authentication issue (explained here). Because of the double hop issue, you will need to reference a username and password within your to connect to the external system (unless anonymous access is allowed). However, hard coding a username and password is not only a bad practice, it's also insecure, unmanageable and lacks portability.

This article focuses on using the SharePoint Secure Store Application to store user credentials for use in code based solutions. Credential information can be retrieved by code to authenticate against other systems, such as Active Directory. Credentials in the Secure Store are stored securely and can be managed via the Central Administration site.

This article assumes you have the SharePoint Secure Store Application configured. To complete the example, you will need access to the Central Administration site, and will need permissions to create new Target Applications and deploy solutions.

The source code for this project can be downloaded from the Microsoft TechNet Gallery, here: Retrieving Credentials from the SharePoint Secure Store using C#

Creating a Target Application in the Secure Store

Before looking at the code, we are going to step through creating a Target Application in the Secure Store.

The new Target Application we create will store a Windows Domain user credential, which we will then use in the code example to authenticate against Active Directory.

1. Browse to the Central Administration site
2. Click on Application Management
3. Click on Manage Service Applications
4. Click the Secure Store Application
5. Create a new Target Application
5.1 On the ribbon, in the Manage Target Applications, click New


5.2 In the Create New Secure Store Target Application page, enter the following information;

Target Application ID: ActiveDirectoryConnection
Display Name: Active Directory Connection
Contact E-mail: (enter your email address)
Target Application Type: Group


5.3 Click Next. On the next page, Specify the credential fields for the Secure Store Target Application, configure the fields that are used to store the credential information.

Configure the following fields:

Field NameField TypeMasked
UserNameWindows UsernameNo
PasswordWindows PasswordYes
DomainKeyNo


5.4 Click Next. On the next page, Specify the membership Settings, you need to enter administrators and members. Administrators are people who will be able to manage this target application, while members are people who will have permissions to retrieve (read) the user credentials.

Since the user credentials will be accessed via code under the context of a standard site user, we are adding Domain Users as Members. The SharePoint farm account (and any other administrators) should be added as administrators.


5.5 Click OK to save the new Target Application.
6. Set the credentials of the new Target Application
6.1 Select the new Target Application, and click Set (in the Credentials section of the ribbon)


6.2 Enter the username, password and domain of the Windows Domain user account that you want to use. This account will be used to connect to, and query, Active Directory.


6.3 Click OK to save the credential information

You have finished creating the new Target Application. The next step is to write some code that will access and use the credentials.

Building a Class to Access the Credentials


This part of the example requires creating a some classes and methods for accessing the secure store, retrieving a credential object, and returning it to the caller.

1. Create a new empty SharePoint Project (deploy as a farm solution)
2. Add the following references to the project:

Microsoft.Office.SecureStore.dll (see the reference below about finding the Microsoft.Office.SecureStore.dll in the GAC (Global Assembly Cache))
Microsoft.BusinessData.dll

3. Add a new class to the project, called SecureStoreProxy
4. Make the class as public and static

namespace SecureStoreCredentialsExample
{
    public static class SecureStoreProxy
    {
    
    }
}


5. Add the following using statements

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using Microsoft.Office.SecureStoreService.Server;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;


4. Add the following code to the SecureStoreProxy class
4.1. Add CredentialType enum. This will be used by the GetCredentialsFromSecureStoreService method.

public enum CredentialType
{
    Domain,
    Generic
}


4.2. Add a new class to store credential information. This class implements IDisposable to ensure the classes SecureString properties are correctly disposed of. It also contains a method for returning a SecureString as a String.

public class UserCredentials : IDisposable
{

    private readonly SecureString _userName;
    public String UserName
    {
        get { return ConvertToUnsecuredString(_userName); }
    }

    public String DomainName;

    private readonly SecureString _password;
    public String Password
    {
        get { return ConvertToUnsecuredString(_password); }
    }
    public UserCredentials(SecureString username, SecureString password)
    {
        _userName = username.Copy();
        _password = password.Copy();
    }

    public UserCredentials(SecureString username, SecureString password, SecureString domain)
    {
        _userName = username.Copy();
        _password = password.Copy();
        DomainName = ConvertToUnsecuredString(domain);
    }

    private static string ConvertToUnsecuredString(SecureString securedString)
    {
        if (securedString == null) return String.Empty;
        IntPtr uString = IntPtr.Zero;
        try
        {
            uString = Marshal.SecureStringToGlobalAllocUnicode(securedString);
            return Marshal.PtrToStringUni(uString);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(uString);
        }
    }

    private Boolean _isDisposed;
    public void Dispose()
    {
        if (_isDisposed) return;
        _userName.Dispose();
        _password.Dispose();
        _isDisposed = true;
    }
}


4.3. Add a new public static method used to retrieve credential information from the Secure Store. This method takes an Application ID (a target application id), and the CredentialType enum as inputs, and returns a UserCredentials object.

public static UserCredentials GetCredentialsFromSecureStoreService(string applicationId, CredentialType credentialType)
{
    ISecureStoreProvider provider = SecureStoreProviderFactory.Create();
    if (provider == null)
    {
        throw new InvalidOperationException("Unable to get an ISecureStoreProvider");
    }

    using (SecureStoreCredentialCollection credentials = provider.GetCredentials(applicationId))
    {
        var un = from c in credentials
                    where c.CredentialType == (credentialType == CredentialType.Domain ? SecureStoreCredentialType.WindowsUserName : SecureStoreCredentialType.UserName)
                    select c.Credential;

        var pd = from c in credentials
                    where c.CredentialType == (credentialType == CredentialType.Domain ? SecureStoreCredentialType.WindowsPassword : SecureStoreCredentialType.Password)
                    select c.Credential;

        var dm = from c in credentials
                    where c.CredentialType == SecureStoreCredentialType.Key
                    select c.Credential;


        SecureString userName = un.First(d => d.Length > 0);
        SecureString password = pd.First(d => d.Length > 0);
        SecureString domain = dm.First(d => d.Length > 0);
        var userCredientals = new UserCredentials(userName, password, domain);
        return userCredientals;
    }
}


The method connects to the Secure Store, and retrieves the credentials of the Target Application. If the user context that the code is running isn't in the membership of the Target Application, an exception will be thrown.

Once the method has connected to the Secure Store and retrieved the Target Application's credentials (returned as a SecureStoreCredentialCollection), it parses the collection, extracting the username, password and domainname into a new UserCredential object.

5. Build the solution.


Building a Webpart that uses the Credentials to Connect to Active Directory


The final step in our example is to build a webpart the retrieves the membership of an Active Directory group.

1. Add a new standard webpart to the project called GetGroupMembership
2. Add a new reference to the project

System.DirectoryServices.AccountManagement

3. Add a the following using statements to the webparts code file

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using System.DirectoryServices.AccountManagement;
using System.Text;
using IdentityType = System.DirectoryServices.AccountManagement.IdentityType;


4. Copy the following code into the webpart file.

The webpart contains a textbox, button and label. When an enduser enters a group name and clicks submit, the webpart will connect to Active Directory, using the credentials retrieved from the Secure Store. It will search for the group, using methods in the System.DirectoryServices.AccountManagement namespace. Finally it will return a list of the groups members, and message indicating if the current user is a member of the group.

Notice how the GetPrinipalContext method retrieves the UserCredients (used to authenticated against Active Directory) from the SharePoint Secure Store, via the Proxy class we created early in this article.

For more information on using the System.DirectoryServices.AccountManagement namespace in a SharePoint project, see this article: SharePoint: Querying Active Directory from a Farm Based Solution using C#.Net

namespace SecureStoreCredentialsExample.GetGroupMembership
{
    [ToolboxItemAttribute(false)]
    public class GetGroupMembership : WebPart
    {
        private Label _results;
        private TextBox _groupName;
        private Button _submit;

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            _results = new Label();
            _groupName = new TextBox();
            _submit = new Button {Text = "Submit"};
            _submit.Click += SubmitOnClick;
        }

        protected override void CreateChildControls()
        {
            Controls.Add(_groupName);
            Controls.Add(_submit);
            Controls.Add(new LiteralControl("<br/>"));
            Controls.Add(_results);
        }

        private void SubmitOnClick(object sender, EventArgs eventArgs)
        {
            try
            {
                StringBuilder output = new StringBuilder();
                GroupPrincipal group = GetGroup(_groupName.Text);
                if (group == null)
                {
                    _results.Text = "Group not found.";
                    return;
                }
                output.Append(String.Format("Current user, {0}, {1} a member of {2}", SPContext.Current.Web.CurrentUser.Name, IsUserMemberOfGroup(group, SPContext.Current.Web.CurrentUser.Sid, IdentityType.Sid) ? "is" : "is not", group.DisplayName));
                output.Append("<br/>");
                var groupMembers = GetAllUsersInGroup(group, true);
                String members = String.Empty;
                foreach (UserPrincipal userPrincipal in groupMembers)
                {
                    members = String.Format("{0}{1}", String.IsNullOrEmpty(members) ? "" : String.Format("{0}, ", members), userPrincipal.DisplayName);
                }
                output.Append(String.Format("The current list of users in the {0} group are: {1}", group.DisplayName, members));
                _results.Text = output.ToString();
            }
            catch (Exception e)
            {
                _results.Text = e.Message;
            }
        }

        private UserPrincipal GetUser(String identity, IdentityType identityType)
        {
            PrincipalContext principalContext = GetPrincipalContext;
            return UserPrincipal.FindByIdentity(principalContext, identityType, identity);
        }

        private IEnumerable<UserPrincipal> GetAllUsersInGroup(GroupPrincipal groupPrincipal, Boolean recurse)
        {
            PrincipalSearchResult<Principal> members = groupPrincipal.GetMembers(recurse);
            return members.OfType<UserPrincipal>().ToList();
        }

        private GroupPrincipal GetGroup(String groupName)
        {
            PrincipalContext principalContext = GetPrincipalContext;
            return GroupPrincipal.FindByIdentity(principalContext, IdentityType.Name, groupName);
        }

        private Boolean IsUserMemberOfGroup(GroupPrincipal groupPrincipal, String identity, IdentityType identityType)
        {
            UserPrincipal userPrincipal = GetUser(identity, identityType);
            if (userPrincipal == null) return false;
            return userPrincipal.IsMemberOf(groupPrincipal);
        }

        private static PrincipalContext GetPrincipalContext
        {
            get
            {
                using (var userCredientals = SecureStoreProxy.GetCredentialsFromSecureStoreService("ActiveDirectoryConnection", SecureStoreProxy.CredentialType.Domain))
                {
                    var principalContext = new PrincipalContext(ContextType.Domain, userCredientals.DomainName, userCredientals.UserName, userCredientals.Password);
                    return principalContext;
                }
            }
        }
    }
}


5. Build and deploy the project
6. Add the webpart to a page
7. Enter an Active Directory group into the text box and click Submit. The webpart will search Active Directory for the group, display a message about the current users membership status, and finally enumerate and list the groups members.



See Also

SharePoint: Querying Active Directory from a Farm Based Solution using C#.Net

References

How to use the DirectoryServices Namespace in ASP
Microsoft.Office.SecureStoreService.dll
Getting credients from the Secure Store Provider
Retrieving Credentials from the SharePoint Secure Store using C#