<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Security Trimming in SharePoint 2013</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:44:25 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Creating a Custom Post-Security Trimmer with SharePoint 2013</title>
      <link>https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/creating-a-custom-post-security-trimmer-with-sharepoint-2013</link>
      <pubDate>Wed, 31 Oct 2012 07:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Sveinar Rasmussen (Principal SDE)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/security_trimming_in_sharepoint_2013/2012/10/31/creating-a-custom-post-security-trimmer-with-sharepoint-2013/</guid>
      <description><![CDATA[What You Will Learn
This blog post will show you how to write your own custom security post-trimmer...]]></description>
      <content:encoded><![CDATA[<h2>What You Will Learn</h2>
<p>This blog post will show you how to write your own custom security post-trimmer for SharePoint 2013. Not only that, we will take you through the steps of deploying and registering&nbsp;the trimmer using a crawl rule before putting&nbsp;the trimmer to work.</p>
<table style="border-width: 3px; border-color: #fff7fd;" border="2" frame="below">
<tbody>
<tr>
<td>
<p>Please visit the official MSDN documentation for the overview and <br />definitive source of documentation of this feature:</p>
<p><a href="https://msdn.microsoft.com/en-us/library/ee819930.aspx">https://msdn.microsoft.com/en-us/library/ee819930.aspx</a></p>
</td>
</tr>
</tbody>
</table>
<h2>Why Post-Security Trimming</h2>
<p>There are two kinds of custom security trimming: Pre-trimming refers to pre-query evaluation where the backend rewrites the query adding security information before the index lookup in the search index. Post-trimming refers to post-query evaluation where the search results are pruned before they are returned to the user.</p>
<p>Post-trimming analyzes each search result after the query has been evaluated. Performance and potential incorrect refiner data and hit counts aside, sometimes it is necessary to perform last-minute security evaluation "when the results are served".</p>
<p>One scenario could be to deny documents in the search results for machines that do not have proper anti-virus software. Another can be to restrict certain documents from being visible in the search results outside a given time of day.</p>
<h2>Requirements</h2>
<ul>
<li>SharePoint 2013 Server</li>
<li>Visual Studio 2012</li>
<li>A Custom Connector sending claims (see previous post on this blog).</li>
</ul>
<h2>The Trimmer Design</h2>
<p>Let's create a simple post-security trimmer. It should remove documents from the search results if a certain hint is given for document results. We will use the string field docaclmeta for this purpose. The rules are simple. Documents will be removed if this field contains the text 'deny'. If this field is empty or contains anything else (e.g. 'allow'), then the documents will be visible in the search results.</p>
<h2>The Code</h2>
<p>This <a title="MSDN article offers" href="https://msdn.microsoft.com/en-us/library/ee819923(v=office.15).aspx#CreatePostTrimmer">MSDN article offers</a> useful starting tips on creating the security post-trimmer project in Visual Studio, adding the references to both the Microsoft.Office.Server.Search.dll and the Microsoft.IdentityModel.dll.</p>
<p>The following is added to the using directives at the beginning of the class file, SearchPostTrimmer.cs:</p>
<pre class="scroll">    using System.Collections.Specialized;
    using System.Security.Principal;
    using Microsoft.Office.Server.Search.Administration;
    using Microsoft.Office.Server.Search.Query;
</pre>
<p>We then define the class as implementing the ISecurityTrimmerPost interface in the class declaration:</p>
<pre class="scroll">public class SearchPostTrimmer : ISecurityTrimmerPost</pre>
<p>We do not need any additional settings for this trimmer to work. Thus, the Initialize method remains empty:</p>
<pre class="scroll">    /// &lt;summary&gt;
    /// Initialize the post-trimmer.
    /// &lt;/summary&gt;
    /// &lt;param name="staticProperties"&gt;Static properties configured for the trimmer.&lt;/param&gt;
    /// &lt;param name="searchApplication"&gt;Search Service Application object&lt;/param&gt;
    public void Initialize(NameValueCollection staticProperties, SearchServiceApplication searchApplication)
    {
        // Intentionally blank
    }    
</pre>
<p>The actual trimmer logic resides in the CheckAccess method. This method returns a bit array with values 1 or 0, grant access or deny access, respectively.</p>
<p>We will check for the term 'deny' in document's docaclmeta field. If so, we will remove that document from the search results.</p>
<pre class="scroll">    /// &lt;summary&gt; 
    /// Check access for the results returned from the search engine
    /// &lt;/summary&gt;
    /// &lt;param name="documentUrls"&gt; List of the URLs for each document whose access is to be determined by the security trimmer implementation.&lt;/param&gt;
    /// &lt;param name="documentAcls"&gt; List of the document ACLs for each document whose access is to be determined by the security trimmer implementation. This list may be null or may contain String.Empty strings.&lt;/param&gt;
    /// &lt;param name="sessionProperties"&gt; Transient property bag valid within the scope of a single query processing component execution.&lt;/param&gt;
    /// &lt;param name="userIdentity"&gt; Identity of the user.&lt;/param&gt;
    /// &lt;returns&gt;a bitarray with values 1 if the respective document identifier from documentUrls has been granted access or 0 if the respective document identifier has not been granted access &lt;/returns&gt;
    public BitArray CheckAccess(
        IList&lt;string&gt; documentUrls, 
        IList&lt;string&gt; documentAcls, 
        IDictionary&lt;string, object&gt; sessionProperties,
        IIdentity passedUserIdentity)
    {
        if ((null == documentCrawlUrls) || (documentCrawlUrls.Count &lt; 1))
        {
            throw new NullReferenceException("Error: CheckAccess method is called with invalid URL list");
        }

        if ((null == documentAcls) || (documentAcls.Count &lt; 1))
        {
            throw new NullReferenceException("Error: CheckAccess method is called with invalid ACL list");
        }

        if (null == passedUserIdentity)
        {
            throw new NullReferenceException("Error: CheckAccess method is called with invalid user identity parameter");
        }
        
        // Initialize the bit array with false value which means all results will be trimmed out.
        var urlStatusArray = new BitArray(documentCrawlUrls.Count, false);
        for (var x = 0; x &lt; documentAcls.Count; x++)
        {
            if (!string.IsNullOrEmpty(documentAcls[x]))
            {
                urlStatusArray[x] = true;
                if (string.Compare(documentAcls[x], "deny", StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    urlStatusArray[x] = false;
                }
            }
            else
            {
                urlStatusArray[x] = true;
            }
        }

        return urlStatusArray;
    }
</pre>
<h3>Performance Considerations&nbsp;</h3>
<p>One might consider the following tips to improve the overall performance with this trimmer&nbsp;as a starting point:</p>
<ul>
<li>Use <a href="https://blogs.msdn.com/b/security_trimming_in_sharepoint_2013/archive/2012/10/30/creating-a-custom-pre-security-trimmer.aspx">Pre-Security trimming</a> instead for improved performance and correctness (hit counts etc.)</li>
<li>Cache locally any data needed to evaluate if a result should be removed or not</li>
</ul>
<h2>Deploying Trimmer</h2>
<p>After you have&nbsp;built the custom security trimmer project, you must deploy it to the global assembly cache on any server in the Query role.</p>
<ol>
<li>On the Start menu, choose <strong>All Programs</strong>, choose <strong>Microsoft Visual Studio 2010</strong>, and then&nbsp;choose <strong>Visual Studio Tools</strong> and open a <strong>Visual Studio command prompt</strong>.</li>
<li>To install the SearchPostTrimmer.dll, type the following the command prompt<br /><br /><strong><span style="font-family: courier new,courier;">gacutil /i &lt;ExtractedFolderPath&gt;\PostTrimmer\bin\Debug\SearchPostTrimmer.dll</span></strong></li>
<li>As the last step of deploying the trimmer, we need to learn about the <strong>token</strong> of the DLL. Type the following the command prompt<br /><br /><strong><span style="font-family: courier new,courier;">gacutil /l SearchPostTrimmer</span></strong><br />Write down the token listed for the newly added DLL.</li>
</ol>
<h2>Registering Trimmer</h2>
<ol>
<li>Open the SharePoint Management Shell.</li>
<li>At the command prompt, type the following command:<br /><br /><span style="font-family: courier new,courier;">New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" </span><br /><span style="font-family: courier new,courier;">-typeName "CstSearchPostTrimmer.SearchPostTrimmer, CstSearchPostTrimmer, </span><br /><span style="font-family: courier new,courier;">Version=1.0.0.0, Culture=neutral, PublicKeyToken=token" -RulePath "xmldoc://*"</span><br /><br />where <em><strong>token</strong></em> is the text copied from the <strong>gacutil /l</strong> command above. The <strong>xmldoc://*</strong>&nbsp;refers to the crawl rule we defined in <a title="indexing custom claims" href="https://blogs.msdn.com/b/security_trimming_in_sharepoint_2013/archive/2012/10/29/creating-custom-connector-sending-claims-to-sharepoint-2013.aspx">the XML Connector blog post</a>. Example: <em>New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" -typeName "CstSearchPostTrimmer.SearchPostTrimmer, SearchPostTrimmer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4ba2b4aceeb50e6d" -RulePath "xmldoc://*" -Id 1</em></li>
<li>Restart the Search Service by typing<br /><br /><span style="font-family: courier new,courier;">net&nbsp;restart sphostcontrollerservice</span></li>
</ol>
<h2>Testing&nbsp;the Trimmer</h2>
<p>Now, you can issue queries and the beauty of the Post-trimmer logic should reveal itself on each query evaluated. Try modifying the docaclmeta field contents in the Product.xml file for the custom connector, issue a full crawl and repeat the query.</p>
<h2>Acknowledgements</h2>
<p><strong>Author:</strong> Sveinar Rasmussen (sveinar).</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/55/87/Trimming/8512.PostTrimmer.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/8512.PostTrimmer.zip">https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/8512.PostTrimmer.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Creating a Custom Pre-Security Trimmer for SharePoint 2013</title>
      <link>https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/creating-a-custom-pre-security-trimmer-for-sharepoint-2013</link>
      <pubDate>Tue, 30 Oct 2012 06:52:00 GMT</pubDate>
      <dc:creator><![CDATA[Sveinar Rasmussen (Principal SDE)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/security_trimming_in_sharepoint_2013/2012/10/30/creating-a-custom-pre-security-trimmer-for-sharepoint-2013/</guid>
      <description><![CDATA[What You Will Learn
This blog post will show you how to write your own custom security pre-trimmer...]]></description>
      <content:encoded><![CDATA[<h2>What You Will Learn</h2>
<p>This blog post will show you how to write your own custom security pre-trimmer for SharePoint 2013. We will take you through the steps of deploying and registering the trimmer before putting the trimmer to work.</p>
<table style="border-width: 3px; border-color: #fff7fd;" border="2" frame="below">
<tbody>
<tr>
<td>
<p>Please visit the official MSDN documentation for the overview and <br />definitive source of documentation of this feature:</p>
<p><a href="https://msdn.microsoft.com/en-us/library/ee819930.aspx">https://msdn.microsoft.com/en-us/library/ee819930.aspx</a></p>
</td>
</tr>
</tbody>
</table>
<h2>Why&nbsp;Use&nbsp;Pre-Security Trimmers</h2>
<p>Pre-trimming refers to pre-query evaluation where the backend rewrites the query adding security information before the index lookup in the search index. Post-trimming refers to post-query evaluation where search results are pruned before they are returned to the user.</p>
<p>We recommend the use of pre-trimming for performance and general correctness; pre-trimming prevents information leakage for refiner data and hit count instances.</p>
<h2>Requirements</h2>
<ul>
<li>SharePoint 2013 Server</li>
<li>Visual Studio 2012</li>
<li>A Custom Connector sending claims (see previous post on this blog)</li>
</ul>
<h2>The Trimmer Design</h2>
<p>Let's create a simple pre-security trimmer. A trimmer that reads&nbsp;group membership data from a text file, performs a user lookup for group membership data and then adds claims to the query tree based upon this. In short, the trimmer code needs to figure out which user that is issuing the query, then perform a group membership lookup on that user and then add claims for that user, depending on the group membership.</p>
<h2>The Code</h2>
<p>This <a title="MSDN article offers" href="https://msdn.microsoft.com/en-us/library/ee819923(v=office.15).aspx#CreatePreTrimmer">MSDN article offers</a> useful starting tips on creating the security pre-trimmer project in Visual Studio, by&nbsp;adding references to both the Microsoft.Office.Server.Search.dll and the Microsoft.IdentityModel.dll.</p>
<p>Add the following to the using directives at the beginning of the class file, SearchPreTrimmer.cs:</p>
<pre class="scroll">    using System.Security.Principal;
    using Microsoft.IdentityModel.Claims;
    using Microsoft.Office.Server.Search.Administration;
    using Microsoft.Office.Server.Search.Query;
</pre>
<p>We then define the class as implementing the ISecurityTrimmerPre interface in the class declaration:</p>
<pre class="scroll">public class XmlContentSourcePreTrimmer : ISecurityTrimmerPre
</pre>
<p>We have to define a few constants at the beginning of the class. These variables may be altered by the static properties given when the trimmer is registered with SharePoint.</p>
<pre class="scroll">    private string _claimType = "https://surface.microsoft.com/security/acl";
    private string _claimIssuer = "customtrimmer";
    private string _claimValueType = ClaimValueTypes.String;
    private string _lookupFilePath = "datafile.txt";
</pre>
<p>The initialization method of the trimmer may modify a few "constant variables", primarily claim type and issuer along with the file path to the input data of this trimmer's group membership data:</p>
<pre class="scroll">    /// &lt;summary&gt;
    /// Initialize the pre-trimmer.
    /// &lt;/summary&gt;
    /// &lt;param name="staticProperties"&gt;Static properties configured for the trimmer.&lt;/param&gt;
    /// &lt;param name="searchApplication"&gt;Search Service Application object&lt;/param&gt;
    public void Initialize(NameValueCollection staticProperties, SearchServiceApplication searchApplication)
    {
        if (staticProperties.Get("claimtype") != null)
        {
            _claimType = staticProperties.Get("claimtype");
        }

        if (staticProperties.Get("claimissuer") != null)
        {
            _claimIssuer = staticProperties.Get("claimissuer");
        }

        if (staticProperties.Get("datafile") != null)
        {
            _lookupFilePath = staticProperties.Get("datafile");
        }

        RefreshDataFile();
    }    
</pre>
<p>The AddAccess method of the trimmer is responsible for returning claims to be added to the query tree. We will refresh the group membership data if needed and&nbsp;figure out the user id for key lookup into the group membership structure from the text file.&nbsp;</p>
<pre class="scroll">    /// &lt;summary&gt; 
    /// Add custom claims to the query tree 
    /// &lt;/summary&gt;
    /// &lt;param name="sessionProperties"&gt;Session properties collection&lt;/param&gt;
    /// &lt;param name="userIdentity"&gt;query user identity&lt;/param&gt;
    /// &lt;returns&gt;An enumerable of tuples with claims&lt;/returns&gt;
    public IEnumerable&lt;Tuple&lt;Claim, bool&gt;&gt; AddAccess(IDictionary&lt;string, object&gt; sessionProperties, IIdentity userIdentity)
    {

        if (null == userIdentity)
        {
            throw new NullReferenceException("Error: AxdAccess method is called with an invalid user identity parameter");
        }

        RefreshDataFile();
        var claims = new LinkedList&lt;Tuple&lt;Claim, bool&gt;&gt;();
        var membership = GetMembership(GetUserId(userIdentity));
        if (membership != null)
        {
            foreach (var member in membership)
            {
                claims.AddLast(new Tuple&lt;Claim, bool&gt;(new Claim(_claimType, member, _claimValueType, _claimIssuer, _claimIssuer), false));
            }
        }

        return claims;
    }</pre>
<p>We need a class to act as a simple wrapper of a Dictionary&lt;string, string&gt;.&nbsp;It should essentially serve as a key-value lookup from a user-ID to its group membership data. Each user's group membership has the form group1;group2;group3, where ";" is used to separate between each group membership entry. This class is simply called <strong>Lookup</strong>.</p>
<pre class="scroll">    private string GetUserId(IIdentity userIdentity)
    {
        // Run through all the claims of the claims identity, look for the
        // user logon name claim and primary SID to get the current user:
        //   domain\username
        ...
        ...

        return strUser;
    }

    private string[] GetMembership(string key)
    {
        lock (_lock)
        {
            var value = _lookup.Get(key);
            if (!string.IsNullOrEmpty(value))
            {
                return value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
            }

            return null;
        }
    }

    private void RefreshDataFile()
    {
        if ((DateTime.Now - _lookupFileStamp).Seconds &gt; 30)
        {
            lock (_lock)
            {
                _lookupFileStamp = DateTime.Now;
                if (File.Exists(_lookupFilePath))
                {
                    _lookup = Lookup.Load(_lookupFilePath);
                }
            }
        }
    }
</pre>
<h3>Performance Considerations&nbsp;</h3>
<p>Consider the following tips to improve the overall performance with this trimmer as&nbsp;a starting point:</p>
<ul>
<li>Use multiple Lookup classes in a hash-table on a given key.</li>
<li>Use a compressible stream and binary serialize the Lookup data.</li>
<li>Use IPC (NetPipe) to talk to a local service that holds the more efficient key-value Lookups.</li>
</ul>
<h2>Deploying Trimmer</h2>
<p>After you have built the custom security trimmer project, you must deploy it to the global assembly cache on any server in the Query role.</p>
<ol>
<li>On the Start menu, choose <strong>All Programs</strong>, choose <strong>Microsoft Visual Studio 2010</strong>, and then choose <strong>Visual Studio Tools</strong> and open a <strong>Visual Studio command prompt</strong>.</li>
<li>To install the SearchPreTrimmer.dll, type the following the command prompt<br /><br /><strong><span style="font-family: courier new,courier;">gacutil /i &lt;ExtractedFolderPath&gt;\PreTrimmer\bin\Debug\SearchPreTrimmer.dll</span></strong></li>
<li>As the last step of deploying the trimmer, we need to learn about the <strong>token</strong> of the DLL. Type the following the command prompt<br /><br /><strong><span style="font-family: courier new,courier;">gacutil /l SearchPreTrimmer</span></strong><br />Write down the token listed for the newly added DLL.</li>
</ol>
<h2>Registering Trimmer</h2>
<ol>
<li>Open the SharePoint Management Shell.</li>
<li>At the command prompt, type the following command:<br /><br /><span style="font-family: courier new,courier;">New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" </span><br /><span style="font-family: courier new,courier;">-typeName "CstSearchPreTrimmer.SearchPreTrimmer, CstSearchPreTrimmer, </span><br /><span style="font-family: courier new,courier;">Version=1.0.0.0, Culture=neutral, PublicKeyToken=token"</span><br /><br />where <em><strong>token</strong></em> is the text copied from the <strong>gacutil /l</strong> command above. Example: <em>New-SPEnterpriseSearchSecurityTrimmer -SearchApplication "Search Service Application" -typeName "CstSearchPreTrimmer.SearchPreTrimmer, SearchPreTrimmer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4ba2b4aceeb50e6d" -Id 2</em></li>
<li>Restart the Search Service by typing<br /><br /><span style="font-family: courier new,courier;">net&nbsp;restart sphostcontrollerservice</span></li>
</ol>
<h2>Testing&nbsp;the Trimmer</h2>
<p>Now, you can issue queries and you can see the beauty of the pre-trimmer logic in action for every query evaluated. Try modifying the datafile.txt for different group membership per user (keep in mind that contents of the data file&nbsp;are only loaded every 30 seconds). The 30 second refresh interval is a defined constant in the code. Enjoy!</p>
<h2>Acknowledgements</h2>
<p><strong>Author:</strong> Sveinar Rasmussen (sveinar).</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/55/87/Trimming/8357.PreTrimmer.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/8357.PreTrimmer.zip">https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/8357.PreTrimmer.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Creating Custom Connector Sending Claims with SharePoint 2013</title>
      <link>https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/creating-custom-connector-sending-claims-with-sharepoint-2013</link>
      <pubDate>Mon, 29 Oct 2012 15:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Sveinar Rasmussen (Principal SDE)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/security_trimming_in_sharepoint_2013/2012/10/29/creating-custom-connector-sending-claims-with-sharepoint-2013/</guid>
      <description><![CDATA[What You Will Learn
This blog entry describes how to take an existing custom XML connector to the...]]></description>
      <content:encoded><![CDATA[<h2 class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3"><span id="ms-rterangecursor-start" rtenodeid="1"></span>What You Will Learn</h2>
<p class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">This blog entry describes how to take an existing custom XML connector to the Search service application, modify the connector to submit security ACLs as claims, install and deploy for SharePoint 2013 and test it. Using this blog post, you can index external sources with its security model within SharePoint itself. Keep reading and we will show you how.</p>
<h2 class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Why Use Claims</h2>
<p class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Claims have the potential to simplify authentication logic for external contents in SharePoint 2013. Claims-based identity enables applications to know a few facts about the user's permission to view content. Thus, we can render legacy or different security models&nbsp;into custom claims in SharePoint 2013.</p>
<p class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Modifying the XML Connector to send ACL information as claims should be helpful to demonstrate how to "light up" external content in search results, i.e. be able to search securely in any content.</p>
<h2 class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Requirements&nbsp;</h2>
<ul>
<li>
<div class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">SharePoint 2013 Server</div>
</li>
<li>
<div class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Visual Studio 2012</div>
</li>
</ul>
<h2 class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">The Starting Point: XML Connector</h2>
<p class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">With <a title="this XML Connector" href="https://blogs.msdn.com/b/xml_connector_for_sharepoint_2013/archive/2012/10/01/xml-connector-for-sharepoint-2013.aspx">this XML Connector </a>as a starting point, we need to modify the connector code as it does not send&nbsp;security claims with its document submissions.&nbsp;</p>
<p class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">The next two sub-sections are "greatly copied" from Anders Fagerhaug's blog post. A&nbsp;huge thank-you and many kudos goes out to him for producing a great starting point for a wonderful custom connector!</p>
<h3 class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3">Install Custom Connector (Thanks Anders!)</h3>
<div class="ExternalClass0866A5A37D0F4DC69D6CACE23EA8BED3"><ol>
<li>Download the zip archive attached to the bottom of this blog entry.</li>
<li>Unzip the contents of the zip archive to a folder on your computer, e.g. C:\CustomSecurityConnector</li>
<li>On the Start menu, choose <strong>All Programs</strong>,&nbsp;choose <strong>Microsoft Visual Studio 2010</strong>, and then&nbsp;choose <strong>Visual Studio Tools</strong> and open a <strong>Visual Studio command prompt</strong>.</li>
<li>To install the XmlFileConnector.dll, type the following command the command prompt<br /><br /><span style="font-family: courier new,courier;"><b><span size="3"><span face="Calibri">gacutil /i &lt;ExtractedFolderPath&gt;\XmlFileConnector\bin\Debug\XmlFileConnector.dll<br /></span></span></b></span><br />This worked if the output after running gacutil is along the lines of "Assembly successfully added to the cache".</li>
<li>Merge the registry entries for the protocol handler by double-clicking on the registry file located at &lt;ExtractedFolderPath&gt;\xmldoc.reg.</li>
<li>On the Start menu, choose <strong>All Programs</strong>, then choose <strong>Microsoft SharePoint 2013 Products</strong> and open a <strong>SharePoint 2013 Management Shell</strong> as an administrator.</li>
<li>To configure the custom XML connector, at the command prompt, type the following command and run it:<br /><br /><strong><span style="font-family: courier new,courier;">$searchapp = Get-SPEnterpriseSearchServiceApplication -Identity "Search Service Application"</span></strong><br /><strong><span style="font-family: courier new,courier;">New-SPEnterpriseSearchCrawlCustomConnector -SearchApplication $searchapp &ndash;Protocol xmldoc -Name xmldoc &ndash;ModelFilePath "&lt;ExtractedFolderPath&gt;\XmlFileConnector\Model.xml"</span></strong> <br /><br />To confirm the configuration, at the command prompt, type the following command and run it:<br /><br /><strong><span style="font-family: courier new,courier;">Get-SPEnterpriseSearchCrawlCustomConnector -SearchApplication $searchapp</span></strong><br /><br />The expected output from this should be a protocol "xmldoc" with a ModeFileLocation pointing to the Model.xml given above.</li>
<li>Finally, we need to restart the search service, type the following commands in the command prompt:<br /><br /><span style="font-family: courier new,courier;"><strong>net stop osearch15<br />net start osearch15</strong></span></li>
</ol></div>
<h3>Create Crawled Property for the Custom XML Connector (Thanks Anders!)</h3>
<p>When the custom XML connector crawls content, the crawled properties discovered during crawl will have to be added to a crawled property category. You need to create this category. <strong>Note:</strong> The user that performs this operation has to be an administrator for the Search service application.</p>
<ol>
<li>On the Start menu, choose <strong>All Programs</strong>, then choose <strong>Microsoft SharePoint 2013 Products</strong> and open <strong>a SharePoint 2013 Management Shell</strong> as an administrator.</li>
<li>To create a new crawled property category, at the command prompt type the following commands and run them, where: &lt;ConnectorName&gt; is the name you want to give the custom XML connector, for example Custom XML Connector:<br /><br />$searchapp = Get-SPEnterpriseSearchServiceApplication -Identity "Search Service Application"<br />New-SPEnterpriseSearchMetadataCategory -Name "&lt;ConnectorName&gt;" -Propset "BCC9619B-BFBD-4BD6-8E51-466F9241A27A" -searchApplication $searchapp<br /><br />The Propset GUID, BCC9619B-BFBD-4BD6-8E51-466F9241A27A, is hardcoded in the file XmlDocumentNamingContainer.cs and should not be changed.</li>
<li>To specify that if there are unknown properties in the newly created crawled property category, these should be discovered during crawl, at the command prompt, type and run the following:<br />
<p>$c = Get-SPEnterpriseSearchMetadataCategory -SearchApplication $searchapp -Identity "&lt;ConnectorName&gt;"<br />$c.DiscoverNewProperties = $true<br />$c.Update()<br /><br />where &lt;ConnectorName&gt; is the name you want to give the custom XML connector, for example Custom XML Connector.</p>
</li>
</ol>
<h3>Create Content Source for the XML Contents (Thanks Anders!)</h3>
<p>To specify what, when and how the XML content should be crawled, you have to create a new content source for your XML content. <strong>Note:</strong> The user that performs this operation has to be an administrator for the Search service application.</p>
<ol>
<li>On the home page of the SharePoint Central Administration website, in the Application Management section, choose <strong>Manage service applications</strong></li>
<li>On the Manage Service Applications page,&nbsp;choose <strong>Search service application</strong>.</li>
<li>On the Search Service Administration Page, in the Crawling section, choose <strong>Content Sources</strong>.</li>
<li>On the Manage Content Sources page, choose <strong>New Content Source</strong>.</li>
<li>On the Add Content Source page, in the Name section, in the Name box, type a name for the new content source, for example XML Connector.</li>
<li>In the Content Source Type section, select Custom repository.</li>
<li>In the Type of Repository section, select xmldoc.</li>
<li>In the Start Address section, in the Type start addresses below (one per line) box, type the address from where the crawler should being crawling the XML content. The start address syntax is different depending on where the XML content is located.<br /><br />XML content is located on a local drive, use the following syntax:<br /><span style="font-family: courier new,courier; font-size: x-small;">xmldoc://localhost/&lt;XMLcontentfolder&gt;/#x=doc:id;;urielm=url;;titleelm=title#</span><br /><br />XML content is located on a network drive, use the following syntax:&nbsp; <br /><span style="font-family: courier new,courier; font-size: x-small;">xmldoc://&lt;SharedNetworkPath&gt;/#x=doc:id;;urielm=url;;titleelm=title#</span><br /><br /><strong>XML content from the supplied example of this blog entry:</strong><br /><span style="font-family: courier new,courier; font-size: x-small;">xmldoc://localhost/C$/CustomSecurityConnector/#x=Product:ID;;titleelm=Title;;urlelm=Url#</span><br /><br />where given that you extracted the zip archive of this blog post to C:\CustomSecurityConnector.</li>
<li>Verify that your newly created content source is shown on the Search Service Application page.</li>
</ol>
<p>The CustomSecurityConnector folder given with the ZIP archive contains Product.xml, which is a sample of a small product catalog.</p>
<h3>Create a Crawl Rule for Content Source</h3>
<p>It is useful to create a crawl rule if we want to do post-Security trimming&nbsp;for these documents.</p>
<ol>
<li>Go to SharePoint Central Admin, choose&nbsp;<strong>Search Administration</strong>.</li>
<li>Under the Crawling section, choose&nbsp;<strong>Crawl Rules</strong> and then <strong>New Crawl Rule</strong></li>
<li>In the Path field, type in <strong>xmldoc://*</strong> which should match up the CrawlUrl of the documents we will crawl in our example Product.xml file. Even if the <strong>Url</strong> in our data states urls along the lines of <strong>https://wfe/site/catalog</strong>, remember that these are the display urls. The SharePoint gatherer/crawler needs the access URL given in the content source setting.<br />
<p><span style="font-family: courier new,courier;">&lt;!--&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;!-- Product&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;!--&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Product&gt;</span><br /><span style="font-family: courier new,courier;">&lt;ID&gt;1&lt;/ID&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Url&gt;<span style="color: #0000ff;"><strong>https://wfe/</strong></span>site/catalog/item1&lt;/Url&gt;</span><br /><span style="font-family: courier new,courier;">...<br /></span><span style="font-family: courier new,courier;">&lt;/Product&gt;</span></p>
</li>
<li>Select&nbsp;"Include all items in this path" and then create the rule by&nbsp;selecting <strong>OK</strong></li>
</ol>
<h3>Crawl</h3>
<p>Finally, start a full crawl of the newly created content source. Then select "View Crawl Logs" when the content source status goes back to the&nbsp;<strong>Idle</strong> status again. You should see&nbsp;11 items successfully crawled.</p>
<h2>Modifying&nbsp;the Connector to Send Custom Claims for Security</h2>
<h3>The Model.xml&nbsp;</h3>
<p>First, we need to change the Model.xml of the connector. To enable sending claims in the connector, we need to submit a binary security descriptor, a boolean to say that we will be providing our own type of security and finally an optional&nbsp;string field (docaclmeta).</p>
<p>Essentially, we need to&nbsp;notify the connector framework of our security&nbsp;field type descriptors plus set a few properties to enable this in the model.</p>
<p>Let's start with the TypeDescriptors first. For every item&nbsp;that we wish to enforce custom security on, we have to set the type descriptors for the following fields:</p>
<ul>
<li><strong>UsesPluggableAuth</strong> as a boolean field type (if the value of this field is true = custom security claims instead of the Windows Security descriptors next)</li>
<li><strong>SecurityDescriptor</strong> as a byte array for the actual encoded claims data</li>
<li><strong>docaclmeta</strong> as an optional&nbsp;string field, which will only be displayed in the search results if populated. This field is not queryable in the index.</li>
</ul>
<p>In the model file itself, the added lines for TypeDescriptors are encapsulated by the XML comments for&nbsp;Claims Security, part 1/2, like this:&nbsp;</p>
<pre class="scroll" spellcheck="false">    &lt;Parameter Name="Return" Direction="Return"&gt;
      &lt;TypeDescriptor Name="Return" TypeName=... &gt;
        &lt;TypeDescriptors&gt;
          &lt;TypeDescriptor Name="Documents" TypeName=...&gt;
            &lt;TypeDescriptors&gt;
              &lt;TypeDescriptor Name="Item" TypeName=...&gt;
                &lt;TypeDescriptors&gt;
                  ...
                  ...

                  &lt;!-- Claims Security Start, part 1/2 --&gt;
                  &lt;TypeDescriptor Name="UsesPluggableAuth" TypeName="System.Boolean" /&gt;
                  &lt;TypeDescriptor Name="SecurityDescriptor" TypeName="System.Byte[]" IsCollection="true"&gt;
                    &lt;TypeDescriptors&gt;
                      &lt;TypeDescriptor Name="Item" TypeName="System.Byte" /&gt;
                    &lt;/TypeDescriptors&gt;
                  &lt;/TypeDescriptor&gt;
                  &lt;TypeDescriptor Name="docaclmeta" TypeName="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /&gt;
                  &lt;!-- Claims Security End  , part 1/2 --&gt;

                  ...
                  ...
</pre>
<p>We are almost done with the model file. The only thing we have left is to supply the field names for the UsesPluggableAuthentication, WindowsSecurityDescriptorField and the DocaclmetaField, like this:</p>
<pre class="scroll" spellcheck="false">  &lt;MethodInstances&gt;
    &lt;Association Name="GetAllDocuments_Instance" Type="AssociationNavigator" ReturnParameterName="Return" ReturnTypeDescriptorPath="Return.Documents"&gt;
      &lt;Properties&gt;
        ...
        ...

        &lt;!-- Claims Security Start, part 2/2 --&gt;
        &lt;Property Name="UsesPluggableAuthentication" Type="System.String"&gt;UsesPluggableAuth&lt;/Property&gt;
        &lt;Property Name="WindowsSecurityDescriptorField" Type="System.String"&gt;SecurityDescriptor&lt;/Property&gt;
        &lt;Property Name="DocaclmetaField" Type="System.String"&gt;docaclmeta&lt;/Property&gt;
        &lt;!-- Claims Security End  , part 2/2 --&gt;
        
        ...
        ...
</pre>
<p>&nbsp;</p>
<h3>The Entities.cs</h3>
<p>We need to modify the corresponding C# code for the objects (i.e. documents) that the connector will submit with custom security ACLs. We have to adjust the Document class with the same fields as specified in the Model.xml:</p>
<ul>
<li>SecurityDescriptor</li>
<li>UsesPluggableAuth</li>
<li>docaclmeta</li>
</ul>
<p>Thus, we added these three properties in the Entities.cs file as shown next:</p>
<pre class="scroll" spellcheck="false">public class Document
{
    private DateTime lastModifiedTime = DateTime.Now;
    public string Title { get; set; }
    public string DocumentID { get; set; }
    public string Url { get; set; }
    public DateTime LastModifiedTime { get { return this.lastModifiedTime; } set { this.lastModifiedTime = value; } }
    public DocumentProperty[] DocumentProperties { get; set; }
    // Security Begin
    public Byte[] SecurityDescriptor { get; set; }
    public Boolean UsesPluggableAuth { get; set; }
    public string docaclmeta { get; set; }
    // Security End
}
</pre>
<h3>The XmlFileLoader.cs</h3>
<p>Finally, we need to modify the connector code to get the input data for the security data, translate this into a corresponding byte array of claims and set the proper field values of the Document class.</p>
<p>Key points to note:</p>
<ul>
<li>The UsesPluggableAuth should only be set to <strong>true</strong> if we indeed will be supplying claim ACLs with this document.</li>
<li>Claims are encoded as a binary byte stream. The data type of this example is always of type string but this is no requirement of the SharePoint backend.</li>
<li>The encoding is done according to the protocol documentation where:<br />- The first byte signals an <strong>allow</strong> or <strong>deny</strong> claim<br />- The second byte is always 1 to indicate that this is a non-NT security ACL (i.e. it is a claim ACL type)<br />- The next four bytes is the size of the following claim value array.<br />- The claim value string follows as a Unicode byte array.<br />- The next four bytes following the claim value array, gives the length of the claim type<br />- The claim type string follows as a Unicode byte array.<br />- The next four bytes following the claim type array, gives the length of the claim data type<br />- The claim data type string follows as a Unicode byte array<br />- The next four bytes following the claim data type array, gives the length of the claim original issuer<br />- The claim issuer string finally follows as a Unicode byte array</li>
</ul>
<h4>The Input XML</h4>
<p>We modified the input XML from the original connector, adding the claimtype, claimvalue, claimissuer and claimaclmeta field to the input:</p>
<p><span style="font-family: courier new,courier;">&lt;!--&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;!-- Product&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;!--&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Product&gt;</span><br /><span style="font-family: courier new,courier;">&lt;ID&gt;1&lt;/ID&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Url&gt;https://wfe/site/catalog/item1&lt;/Url&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Title&gt;Adventure Works Laptop15.4W M1548 White&lt;/Title&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Item_x0020_Number&gt;1010101&lt;/Item_x0020_Number&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Group_x0020_Number&gt;10101&lt;/Group_x0020_Number&gt;</span><br /><span style="font-family: courier new,courier;">&lt;ItemCategoryNumber&gt;101&lt;/ItemCategoryNumber&gt;&lt;ItemCategoryText&gt;Laptops&lt;/ItemCategoryText&gt;</span><br /><span style="font-family: courier new,courier;">&lt;About&gt;Laptop with&nbsp;... &lt;/About&gt;</span><br /><span style="font-family: courier new,courier;">&lt;UnitPrice&gt;$758,00&lt;/UnitPrice&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Brand&gt;Adventure Works&lt;/Brand&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Color&gt;White&lt;/Color&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Weight&gt;3.2&lt;/Weight&gt;</span><br /><span style="font-family: courier new,courier;">&lt;ScreenSize&gt;15.4&lt;/ScreenSize&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Memory&gt;1000&lt;/Memory&gt;</span><br /><span style="font-family: courier new,courier;">&lt;HardDrive&gt;160&lt;/HardDrive&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Campaign&gt;0&lt;/Campaign&gt;</span><br /><span style="font-family: courier new,courier;">&lt;OnSale&gt;1&lt;/OnSale&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Discount&gt;-0.2&lt;/Discount&gt;</span><br /><span style="font-family: courier new,courier;">&lt;Language_x0020_Tag&gt;en-US&lt;/Language_x0020_Tag&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;!-- Security Begin --&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;claimtype&gt;https://surface.microsoft.com/security/acl&lt;/claimtype&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;claimvalue&gt;user1&lt;/claimvalue&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;claimissuer&gt;customtrimmer&lt;/claimissuer&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;claimaclmeta&gt;access&lt;/claimaclmeta&gt;</span><br /><span style="color: #0000ff; font-family: courier new,courier;">&lt;!-- Security End&nbsp;&nbsp; --&gt;</span><br /><span style="font-family: courier new,courier;">&lt;/Product&gt;</span></p>
<p>With this sample input in XML, we have to modify the connector to pick up these extra&nbsp;XML tags.</p>
<p>We will create&nbsp;a GetXml method&nbsp;for this. For instance, we could illustrate one GetXml method where</p>
<ul>
<li>the first parameter is the tag name in the XML (e.g. "claimtype")</li>
<li>the second parameter is the default return value for the GetXml method if the tag does not exist and finally,</li>
<li>the third parameter is the XML element.</li>
</ul>
<pre class="scroll" spellcheck="false">    var claimType = GetXml(documentAclClaimTypeElmName, "https://demo.sharepoint.com/acl", elm);
    var claimValue = GetXml(documentAclClaimValueElmName, "user1", elm);
    var claimIssuer = GetXml(documentAclClaimIssuerElmName, "windows", elm);
    var docAclMeta = GetXml(documentAclMetaElmName, null, elm);
</pre>
<p>With these variables properly filled out from the XML data, we need a method to transform these variables into a claims byte array. We will call this method GetSecurityAcl:</p>
<pre class="scroll" spellcheck="false">    private static byte[] GetSecurityAcl(
        string claimtype, 
        string claimvalue, 
        string claimissuer)
    {
        Byte[] spAcl = null;
        if (!string.IsNullOrEmpty(claimtype) &amp;&amp; 
            !string.IsNullOrEmpty(claimvalue) &amp;&amp; 
            !string.IsNullOrEmpty(claimissuer))
        {
            using (var aclStream = new MemoryStream())
            {
                var dest = new BinaryWriter(aclStream);
                AddClaimAcl(dest, false, claimtype, claimvalue, claimissuer);
                dest.Flush();
                spAcl = aclStream.ToArray();
            }
        }

        return spAcl;
    }
</pre>
<p>We need a method AddClaimAcl to&nbsp;add an encoded claim to a given byte stream:</p>
<pre class="scroll" spellcheck="false">    private static void AddClaimAcl(
        BinaryWriter dest,
        bool isDeny,
        string claimtype,
        string claimvalue,
        string claimissuer)
    {
        const string datatype = @"https://www.w3.org/2001/XMLSchema#string";

        if (string.IsNullOrEmpty(claimvalue))
        {
            return;
        }

        dest.Write(isDeny ? (byte)1 : (byte)0); // Allow = 0, Deny = 1
        dest.Write((byte)1); // Indicate that this is a non-NT claim type

        //
        // Claim Value
        //
        dest.Write((Int32)claimvalue.Length);
        dest.Write(Encoding.Unicode.GetBytes(claimvalue));

        //
        // Claim Type
        //
        dest.Write((Int32)claimtype.Length);
        dest.Write(Encoding.Unicode.GetBytes(claimtype));

        //
        // Claim Data Value Type

        //
        dest.Write((Int32)datatype.Length);
        dest.Write(Encoding.Unicode.GetBytes(datatype));

        //
        // Claim Original Issuer
        //
        dest.Write((Int32)claimissuer.Length);
        dest.Write(Encoding.Unicode.GetBytes(claimissuer));
    }
</pre>
<p>Now we have all our variables filled out. Next, we will set the properties of the Document class using these variables:</p>
<pre class="scroll" spellcheck="false">    var security = GetSecurityAcl(claimType, claimValue, claimIssuer);
    var doc = new Document
    {
        DocumentID = id,
        LastModifiedTime = fileModifiedTime,
        UsesPluggableAuth = security != null,
        SecurityDescriptor = security,
        docaclmeta = docAclMeta
    };

    docs.Add(doc);
</pre>
<p>That is really it. The&nbsp;connector will&nbsp;read the XML security information, encode the claims and supply the optional docaclmeta field before sending this to the SharePoint 2013 indexing backend.</p>
<p>The <a title="pre trimming" href="https://blogs.msdn.com/b/security_trimming_in_sharepoint_2013/archive/2012/10/30/creating-a-custom-pre-security-trimmer.aspx">next blog post will outline</a> how to write a custom pre-security trimmer to unlock these documents for users who are entitled to view it.</p>
<h2>Acknowledgements</h2>
<p><strong>Author:</strong> Sveinar Rasmussen (sveinar)</p>
<p>A <strong>huge thank-you</strong> goes out to <strong>Anders Fagerhaug (andersfa)</strong> and <strong>Armen Kirakosyan (armenk)</strong> for the <a title="original Custom XML Connector blog entry" href="https://blogs.msdn.com/b/xml_connector_for_sharepoint_2013/archive/2012/10/01/xml-connector-for-sharepoint-2013.aspx">original Custom XML Connector blog entry</a>.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/55/87/Trimming/6786.Connector.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/6786.Connector.zip">https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-55-87-Trimming/6786.Connector.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Custom Security Trimming for Search in SharePoint 2013</title>
      <link>https://docs.microsoft.com/archive/blogs/security_trimming_in_sharepoint_2013/custom-security-trimming-for-search-in-sharepoint-2013</link>
      <pubDate>Sun, 28 Oct 2012 07:12:00 GMT</pubDate>
      <dc:creator><![CDATA[Sveinar Rasmussen (Principal SDE)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/security_trimming_in_sharepoint_2013/2012/10/28/custom-security-trimming-for-search-in-sharepoint-2013/</guid>
      <description><![CDATA[Security Trimming
Welcome to this tiny blog on custom security trimming for search in SharePoint...]]></description>
      <content:encoded><![CDATA[<h2>Security Trimming</h2>
<p>Welcome to this tiny blog on custom security trimming for search in SharePoint 2013.</p>
<table style="border-width: 3px; border-color: #fff7fd;" border="2" frame="below">
<tbody>
<tr>
<td>
<p>Please visit the official MSDN documentation for the overview and <br />definitive source of documentation of this feature:</p>
<p><a href="https://msdn.microsoft.com/en-us/library/ee819930.aspx">https://msdn.microsoft.com/en-us/library/ee819930.aspx</a></p>
</td>
</tr>
</tbody>
</table>
<p>Typical scenarios to support could be to enable searching securely in 3rd-party content sources.</p>
<p>Back in SharePoint 2010 only post-trimming was offered for solving this scenario. Post-trimming refers to post-query evaluation where search results are pruned before they are returned to the user. With the new&nbsp;and shiny SharePoint 2013, we have now two types of security trimmers in our toolbox:</p>
<ul>
<li>Pre-Trimmer</li>
<li>Post-Trimmer</li>
</ul>
<p>The new pre-trimmer type is where the trimmer logic is invoked pre-query evaluation. The search server backend rewrites the query adding security information before the index lookup in the search index.</p>
<p>It is our&nbsp;hope to present you with explanations and examples showing you:</p>
<ul>
<li>How to take an existing connector, modify it to submit our own security ACLs as security claims directly, install it and deploy it</li>
<li>How to write and deploy a custom security pre-trimmer</li>
<li>How to write and deploy a custom security post-trimmer</li>
</ul>
<p>I will write up a few blog posts on this in the coming days. <br />Stay tuned!</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
  </channel>
</rss>