Walkthrough: Using a Custom Security Trimmer for SharePoint Server Search Results

Applies to: SharePoint Server 2010

In this article
Prerequisites
Step 1: Create the Custom Security Trimmer
Step 2: Register the Custom Security Trimmer
Step 3 (Optional): Specify a Configurable Limit on the Number of Crawl URLs That Are Checked

Microsoft SharePoint Server 2010 search performs query-time security trimming of search results. However, there may be scenarios in which you want to perform custom security trimming. SharePoint Server search provides support for these scenarios through the ISecurityTrimmer2 interface in the Microsoft.Office.Server.Search.Query namespace.

By implementing this interface, you can create a component to perform custom security trimming of search results before they are returned to the user. The security trimmer registration process enables you to specify configuration properties for the custom security trimmer.

This walkthrough guides you through the steps to implement—create, deploy, and register—a basic custom security trimmer for SharePoint Server search by using Microsoft Visual Studio 2010. It also includes an optional step that shows you how to implement a configurable limit on the number of content items that are checked by the trimmer.

  • Prerequisites

  • Step 1: Create the Custom Security Trimmer

  • Step 2: Register the Custom Security Trimmer

  • Step 3 (Optional): Specify a Configurable Limit on the Number of Crawl URLs That Are Checked

Prerequisites

To complete this walkthrough, you must have the following installed in your development environment:

  • Microsoft SharePoint Server 2010

  • Microsoft Visual Studio 2010 or a similar Microsoft .NET Framework-compatible development tool

Step 1: Create the Custom Security Trimmer

Creating the custom security trimmer includes the following tasks:

  • Setting Up the Custom Security Trimmer Project

  • Writing the Custom Security Trimmer Code

  • Deploying the Custom Security Trimmer Project

  • Example

Setting Up the Custom Security Trimmer Project

In this step, you'll create the custom security trimmer project, and then add the required references and the custom security trimmer class file to the project.

To create the project for the custom security trimmer

  1. In Visual Studio, on the File menu, point to New, and then click Project.

  2. In Project types, under C#, select SharePoint.

  3. Under Templates, select Empty SharePoint Project. In the Name field, type CustomSecurityTrimmerSample, and then click OK.

  4. In the SharePoint Customization Wizard, choose Deploy as a farm solution, and then click Finish.

To add references to the custom security trimmer project

  1. On the Project menu, click Add Reference.

  2. On the .NET tab, select the references with the following component names, and then click OK:

    • Microsoft Search component

      You should see two entries on the .NET tab with the component name Microsoft Search component. Select the entry where the Path column is \ISAPI\Microsoft.Office.Server.Search.dll. If this entry is missing from the .NET tab of the Add References dialog box, you must add the reference from the Browse tab by using the path to the Microsoft.Office.Server.Search.dll.

    • Microsoft.IdentityModel

      If Microsoft.IdentityModel is not listed on the .NET tab, you must add the reference to the Microsoft.IdentityModel.dll from the Browse tab, by using the path:

      %ProgramFiles%\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5.

To create the class file for the security trimmer

  1. On the Project menu, click Add New Item.

  2. Under Visual C# Items in Installed Templates, click Code, and then click Class..

  3. Type CustomSecurityTrimmer.cs, and then click Add

Writing the Custom Security Trimmer Code

Your custom security trimmer must implement the ISecurityTrimmer2 interface. The code example in this section is a basic implementation of this interface.

To modify the default code in CustomSecurityTrimmer

  1. Add the following using directives at the beginning of the class:

    using System;
    using System.Collections.Generic;
    using Microsoft.Office.Server.Search.Query;
    using Microsoft.Office.Server.Search.Administration;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Security.Principal;
    
  2. Specify that the CustomSecurityTrimmer class implements the ISecurityTrimmer2 interface in the class declaration, as follows.

    public class CustomSecurityTrimmer : ISecurityTrimmer2
    

You are now ready to write the code to implement the ISecurityTrimmer2 interface methods.

To implement the ISecurityTrimmer2 interface methods

  1. Add the following code for the Initialize() method declaration.

    public void Initialize(NameValueCollection staticProperties, SearchServiceApplication searchApplication)
    {
    
    }
    

    The basic version of this sample does not include any code in the Initialize method. The Initialize method in the Step 3 (Optional): Specify a Configurable Limit on the Number of Crawl URLs That Are Checked contains an example implementation.

    For more information about implementing the Initialize method, see Writing a Custom Security Trimmer for SharePoint Server Search.

  2. Add the following code for the CheckAccess() method declaration.

    public BitArray CheckAccess(IList<String> documentCrawlUrls, IDictionary<String, Object> sessionProperties, IIdentity passedUserIdentity)
    {
    //CheckAccess method implementation, see steps 3-5.
    }
    
  3. For the first part of the CheckAccess method implementation, declare and initialize a BitArray variable to store the results of the access check for each URL in the crawlURLs collection, and retrieve the user who submitted the query, as follows.

    BitArray retArray = new BitArray(documentCrawlUrls.Count);
    //Use passedUserIdentity to get the identity of the user who issued the query.
    //IClaimsIdentity claimsIdentity = (IClaimsIdentity)passedUserIdentity;
    //IClaimsIdentity is defined in Microsoft.IdentityModel.Claims;
    
  4. Loop through each crawl URL in the collection, and perform the access check to determine if the user who submitted the query can access the crawl URL's associated content item.

    If the user has access to the content item, set the value of the BitArray item at that index, retArray[x], to true; otherwise, set it to false, as follows.

    for (int x = 0; x < documentCrawlUrls.Count; x++)
    {
        /*
          To fully implement the security trimmer, add code to perform 
          the security check and determine if the user can access documentCrawlUrls[x]. 
          If the user can access documentCrawlUrls[x], then:
        */
    
            retArray[x] = true;
        //If not:
            retArray[x] = false;
    }
    
  5. Set the return value of the CheckAccess method to retArray, as follows.

    return retArray;
    

Deploying the Custom Security Trimmer Project

The deployment process does the following:

  • Builds the CustomSecurityTrimmerSample.dll and deploys it to the global assembly cache

  • Recycles Internet Information Services (IIS)

To deploy the custom security trimmer project

  1. On the Build menu, click Deploy Solution.

  2. If you make changes to the project, select Deploy Solution again to automatically retract the previous version of the solution and replace it with the newest version.

Example

Following is the complete sample code for the CustomSecurityTrimmerSample class, described in this step.

using System;
using System.Collections.Generic;
using Microsoft.IdentityModel;
using Microsoft.IdentityModel.Claims;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.Administration;
using System.Collections;
using System.Collections.Specialized;
using System.Security.Principal;

namespace CustomSecurityTrimmerSample
{
    class CustomSecurityTrimmer : ISecurityTrimmer2
    {
        public void Initialize(NameValueCollection staticProperties, SearchServiceApplication searchApplication)
        {
        }

        public BitArray CheckAccess(IList<String> documentCrawlUrls, IDictionary<String, Object> sessionProperties, IIdentity passedUserIdentity)
        {
            BitArray retArray = new BitArray(documentCrawlUrls.Count);
            //Use passedUserIdentity to get the identity of the user who issued the query.
            //IClaimsIdentity claimsIdentity = (IClaimsIdentity)passedUserIdentity;
           //IClaimsIdentity is defined in Microsoft.IdentityModel.Claims;
            for (int x = 0; x < documentCrawlUrls.Count; x++)
            {
              /*
                To fully implement the security trimmer,
                add code to perform the security check 
                and determine if userIdentity can access documentCrawlUrls[x].
                If userIdentity can access documentCrawlUrls[x], then:
               */
                retArray[x] = true;
              //If not:
                retArray[x] = false;
            }
            return retArray;
        }
    }
}

Step 2: Register the Custom Security Trimmer

Step 2 describes how to configure the custom security trimmer, and includes the following tasks:

  • Creating the crawl rule for the custom security trimmer

  • Registering the custom security trimmer for the Search service application and re-crawling the content source

Creating a Crawl Rule for the Custom Security Trimmer

Before you register the custom security trimmer, you must create the crawl rule for the content that the custom security trimmer applies to.

To create the crawl rule

  1. Open SharePoint 2010 Central Administration, and then navigate to the Application Management page.

  2. In the Service Applications section of the Application Management page, click Manage service applications.

  3. Click Search Service Application.

  4. To open the Manage Crawl Rules page, click Crawl rules in the Crawling section.

  5. Click New Crawl Rule, and then for Path, type file://FileServer1/*, replacing FileServer1 with the path to the content to crawl.

  6. For Crawl Configuration, select Include all items in this path, and then click OK to create the crawl rule.

Registering the Custom Security Trimmer and Re-crawl the Content Source

You use the SharePoint 2010 Management Shell to register a custom security trimmer. The following procedure shows how to register a custom security trimmer, with the ID set to 1 for the Search service application, applied to content located on file shares for a server named FileServer1.

To register the custom security trimmer

  1. In Windows Explorer, locate CustomSecurityTrimmerSample.dll in the path <Local_Drive>:\WINDOWS\assembly.

  2. Right-click the file, and then click Properties.

  3. On the General tab in the Properties dialog box, select the token and copy it.

  4. Open the SharePoint 2010 Management Shell. For information about using this tool, see Administering Service Applications Using the SharePoint 2010 Management Shell.

  5. At the command prompt, type the following command.

    New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" 
    -typeName "CustomSecurityTrimmerSample.CustomSecurityTrimmer, CustomSecurityTrimmerSample, 
    Version=1.0.0.0, Culture=neutral, PublicKeyToken=token" -RulePath file://FileServer1/* -id 1
    

    In the command, replace token with the Public Key Token for the CustomSecurityTrimmerSample.dll file, and replace FileServer1 with file share server name.

    Important

    If you have multiple Web front-end (WFE) servers, you must deploy your security trimmer to the global assembly cache on all the WFE servers in the farm.

  6. Initiate a full crawl of the content source, as described in the following procedure.

    To initiate a full crawl of the content source

    1. Open SharePoint 2010 Central Administration, and then navigate to the Application Management page.

    2. In the Service Applications section of the Application Management page, click Manage service applications.

    3. Click Search Service Application.

    4. To open the Manage Content Sources page, click Content Sources in the Crawling section.

    5. For the content source that represents the content affected by the crawl rule for the security trimmer, click the arrow next to the Edit menu. From the menu, select Start Full Crawl. If the content source is not listed on the Manage Content Sources page, you must create it.

      To create the content source

      1. Click New Content Source, and then for Name, type the content source name.

      2. For Content Source Type, click File Shares.

      3. For Start Addresses, type file://FileServer1/*.

      4. Check Start full crawl of this content source, and then click OK to add the content source.

Step 3 (Optional): Specify a Configurable Limit on the Number of Crawl URLs That Are Checked

When you register a custom security trimmer by using the SharePoint Management Shell, you can specify optional configuration properties with the New-SPEnterpriseSearchSecurityTrimmer cmdlet. This cmdlet supports creating a custom security trimmer with configurable settings.

For example, you could create a custom security trimmer with a configurable limit on the number of items checked by the security trimmer. Although this is optional, we recommend that you include some type of limit in your custom security trimmer implementation. For more information, see Writing a Custom Security Trimmer for SharePoint Server Search.

Step 3 describes how to create and register a custom security trimmer with a configurable limit on the number of documents checked, and includes the following tasks:

  • Coding the Custom Security Trimmer

  • Registering the Custom Security Trimmer

  • Example

The custom security trimmer described in this sample is similar to the one described in Step 1: Create the Custom Security Trimmer and Step 2: Register the Custom Security Trimmer. In this custom security trimmer, however, we include additional steps to set a limit in the implementation, and change the command you use to register the custom security trimmer.

Coding the Custom Security Trimmer

Before starting the task described here, create the custom security trimmer project and modify the CustomSecurityTrimmer class, as described in Step 1: Create the Custom Security Trimmer.

To code the custom security trimmer

  1. After you modify the class declaration to implement the ISecurityTrimmer2 interface, declare a variable for the limit value, as follows.

    class CustomSecurityTrimmer : ISecurityTrimmer2
    {
        /*
           Sets a default limit of 200. You can change this 
           to a value that meets your specific requirements.
        */ 
        private int intCheckLimit = 200;
    
  2. Create a method to compare the number of items checked to the configured limit, add the following code.

    private bool CheckLimit(IDictionary<String, Object> sessionProperties, int numChecks)
    {
        Object currentCount;
        sessionProperties.TryGetValue("currentCheckCount", out currentCount);
        if (currentCount == null)
        {
            sessionProperties["currentCheckCount"] = numChecks;
            return (true);
        }
        int currentCountInt = Convert.ToInt32(currentCount);
        currentCountInt += numChecks;
        sessionProperties["currentCheckCount"] = currentCountInt;
        if (currentCountInt <= intCheckLimit)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
  3. To complete the code for the custom security trimmer sample, you must modify the Initialize() and CheckAccess() method implementations described in Step 1: Create the Custom Security Trimmer.

To modify ISecurityTrimmer2 interface method implementations

  1. Add the following code to the Initialize() method.

    if (staticProperties["CheckLimitProperty"] != null)
    {
       intCheckLimit = Convert.ToInt32(staticProperties["CheckLimitProperty"]);
    }
    

    This code sets the limit for the maximum number of documents that the security trimmer will check to the value of a configuration property named CheckLimitProperty.

    Note

    You can specify configuration properties when you register the security trimmer.

  2. Add the following code to the CheckAccess() method, immediately after the method declaration.

    if (!this.CheckLimit(sessionProperties, documentCrawlUrls.Count))
    {
        throw (new PluggableAccessCheckException("<Display Message>"));
    }
    

    This code calls the CheckLimit method. If the limit has been reached, this method returns false. When this occurs, a PluggableAccessCheckException exception is thrown, however the user will not see the message specified in the constructor. After the exception is thrown by the security trimmer, the trimmerID that caused the exception will be filtered out and that result will be excluded from the search results. Other security trimmed results, if available, will be returned and displayed in the search results user interface.

Note

This section does not repeat steps already specified in Step 1 and 2; it only describes the tasks that are different. For information from the previous steps, see Step 1: Create the Custom Security Trimmer and Step 2: Register the Custom Security Trimmer.

Registering the Custom Security Trimmer

Before you start this step, you must deploy CustomSecurityTrimmerSample.dll to the global assembly cache, as described in Step 2: Register the Custom Security Trimmer. Then, you can register the custom security trimmer by using the SharePoint 2010 Management Shell.

The following procedure shows how to register a custom security trimmer with the ID set to 1 for the Search service application, which is applied to content located on file shares for a server named FileServer1. It also sets the value of the CheckLimitProperty configuration setting to 300.

To register the custom security trimmer with the CheckLimitProperty configuration setting

  1. Open the SharePoint 2010 Management Shell. For information about using this tool, see Administering Service Applications Using the SharePoint 2010 Management Shell.

  2. At the command prompt, type the following command.

    New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" 
    -typeName "CustomSecurityTrimmerSample.CustomSecurityTrimmer, CustomSecurityTrimmerSample, 
    Version=1.0.0.0, Culture=neutral, PublicKeyToken=token" 
    -RulePath file:// FileServer1/* -id 1 -properties CheckLimitProperty~300
    

    In the command, replace token with the Public Key Token for the CustomSecurityTrimmerSample.dll file, and replace FileServer1 with file share server name.

Example

Following is the complete sample code for the CustomSecurityTrimmerSample class described in this step.

using System;
using System.Collections.Generic;
using Microsoft.Office.Server.Search.Query;
using Microsoft.Office.Server.Search.Administration;
using System.Collections;
using System.Collections.Specialized;
using System.Security.Principal;

namespace CustomSecurityTrimmerSample
{
    class CustomSecurityTrimmer : ISecurityTrimmer2
    {
        /*        
        Sets a default limit of 200. You can change this 
        to a value that meets your specific requirements.
        */ 
        private int intCheckLimit = 200;

        public void Initialize(NameValueCollection staticProperties, SearchServiceApplication searchApplication)
        {
            if (staticProperties["CheckLimitProperty"] != null)
            {
               intCheckLimit = Convert.ToInt32(staticProperties["CheckLimitProperty"]);
            }

        }

        public BitArray CheckAccess(IList<String> documentCrawlUrls, IDictionary<String, Object> sessionProperties, IIdentity passedUserIdentity)
        {
            if (!this.CheckLimit(sessionProperties, documentCrawlUrls.Count))
            {
                throw (new PluggableAccessCheckException("<Display Message>"));
            }

            BitArray retArray = new BitArray(documentCrawlUrls.Count);
            IIdentity userIdentity = System.Threading.Thread.CurrentPrincipal.Identity;

            for (int x = 0; x < documentCrawlUrls.Count; x++)
            {
              /*
                To fully implement the security trimmer,
                add code to perform the security check 
                and determine if userIdentity can access documentCrawlUrls[x].
                If userIdentity can access documentCrawlUrls[x], then:
               */
                retArray[x] = true;
              //If not:
                retArray[x] = false;
            }
            return retArray;
        }

        private bool CheckLimit(IDictionary<String, Object> sessionProperties, int numChecks)
        {
            Object currentCount;
            sessionProperties.TryGetValue("currentCheckCount", out currentCount);
            if (currentCount == null)
            {                
                sessionProperties["currentCheckCount"] = numChecks;
                return (true);
            }
            int currentCountInt = Convert.ToInt32(currentCount);
            currentCountInt += numChecks;
            sessionProperties["currentCheckCount"] = currentCountInt;            
            if (currentCountInt <= intCheckLimit)
            {
                return true;            
            }            
            else
            {
                return false;
            }
        }
    }
}

See Also

Reference

Microsoft.Office.Server.Search.Query.ISecurityTrimmer2

Microsoft.Office.Server.Search.Query.PluggableAccessCheckException

Concepts

Writing a Custom Security Trimmer for SharePoint Server Search