Skip to main content

Cloud

Social Security Trimmer in SharePoint 2010

Social component in SharePoint 2010 is actively used in intranets. Users share thoughts, links and documents with other users using the functionality provided by SharePoint 2010. Users can tag pages which are a part of the intranet site or they can tag links which are external. Anytime any of the above actions occur, an activity is created. The activities created are displayed to users where ever the “Recent Activities” web part is placed. The web part gathers all the activities depending on subscriptions and displays it to the user.
Here is a link which describes when an activity is displayed or hidden.
(http://technet.microsoft.com/en-us/library/ff608006.aspx)
I will focus mainly on one of the ways the activities are trimmed – Security Trimmed.
An excerpt from the article:

“Security trimming

Adding a tag, a note, or a rating to a Web page creates an activity. Before SharePoint Server displays an activity, it uses a component called the security trimmer to determine whether the current user has permission to view the Web page that the activity applies to. If the user is not permitted to view the Web page, SharePoint Server does not display the activity.
As the search service crawls Web pages, it records the permissions that are required to view each Web page. The security trimmer uses this information to determine whether a given user has permission to view a specific Web page. If the security trimmer has insufficient information to determine whether a user has permission to view a Web page, it errs on the side of caution and reports that the user does not have permission to view the Web page. As a result, if the search service has not crawled a Web page, activities that relate to that Web page will not be displayed.”
And
“If SharePoint Server’s security trimmer has insufficient information to determine whether a user has permission to view a Web page, it errs on the side of caution and reports that the user does not have permission. One result of this behavior is that tags, notes, and ratings that are added to external Web sites are always trimmed.”
This works great when you don’t want to share tags that are used to tag external sites. The default trimmer will trim the links which are external to the web application. This is a huge problem if you are trying to encourage people to tag links whether they are external or internal. The links could come from an RSS feed or links that users feel are relevant to their day to day activities.
This implies that we cannot use the default trimmer. This leaves us with three options:
1. Disable Social Security Trimming.
2. Use the options provided in SharePoint 2010 SP1
3. Write a custom Social Security Trimmer.
Let’s consider these options:
1. Disable Social Security Trimming:
If we disable there are some obvious pitfalls. Any link that the user does not have permissions to access will be displayed. This means they could be a lot of “Request Access” emails and “Why don’t I have access to a certain site/page/list?” questions which don’t always go well. I am sure no one wants to see link to a site called “People to get fired” in their activity list to which they don’t have access. Smile
If you application is open to all and you think disabling the trimming will work for you then use the following PowerShell command to disable the trimmer.
$upaProxyId = (Get-SPServiceApplicationProxy |? {$_.DisplayName -eq “<Name of your user profile SA proxy”}).Id
Remove-SPPluggableSecurityTrimmer -UserProfileApplicationProxyId $upaProxyId -PlugInId 0
2. Options in SP1: There are some additional options available now.
@harbars sums up the options in his article : http://www.harbar.net/archive/2011/07/06/328.aspx
3. Custom Social Security Trimmer:
Since disabling the security trimming might not work in every situation, it makes more sense to write a social security trimmer.
The following article is a good starting point to writing your own security trimmer.
http://msdn.microsoft.com/library/microsoft.office.server.socialdata.isocialsecuritytrimmer(v=office.14).aspx
I will dissect the code and show where in the code changes need to be made to achieve what we need to. (Goal: To Not trim external links)

 
public sealed class CustomSocialSecurityTrimmer : SearchSocialSecurityTrimmer
{
private const string C_KEY_EXCLUDEPATHS = "ExcludePaths";
private const string C_KEY_INCLUDEPATHS = "IncludePaths";
private static object syncRoot = new Object();
private static volatile List<Regex> m_excludePaths;
private static volatile List<Regex> m_includePaths;
/*
These are two lists which hold the 
paths which will be excluded or included from the trimming process.
*/
#region ISocialSecurityTrimmer interface
/*
This function is called every time 
the activities are rendered. Hence cache the list of paths. When you register 
the trimmer, you have to pass in a name value collection of two entries, one for 
the paths that should be included and one for the paths that should be excluded 
from trimming. 
*/
public override void Initialize(NameValueCollection CustomProperty)
{
lock (syncRoot)
{
if (m_includePaths != null)
return;
m_includePaths = new List<Regex>();
m_excludePaths = new List<Regex>();
// Cache parsed Url Regex objects's list onto static area to improve 
performance.
// If you change the properties, you need to restart IIS to clear cache.
string strIncludePaths = CustomProperty[C_KEY_INCLUDEPATHS];
string strExcludePaths = CustomProperty[C_KEY_EXCLUDEPATHS];
if (!String.IsNullOrEmpty(strIncludePaths))
{
string[] strArray = strIncludePaths.Split(new string[] { Environment.NewLine
}, StringSplitOptions.RemoveEmptyEntries);
foreach (string strRegex in strArray)
{
m_includePaths.Add(new Regex(strRegex, RegexOptions.Singleline |
RegexOptions.IgnoreCase));
}
}
if (!String.IsNullOrEmpty(strExcludePaths))
{
string[] strArray = strExcludePaths.Split(new string[] { Environment.NewLine
}, StringSplitOptions.RemoveEmptyEntries);
foreach (string strRegex in strArray)
{
m_excludePaths.Add(new Regex(strRegex, RegexOptions.Singleline |
RegexOptions.IgnoreCase));
}
}
}
}
/* This is the function that will get called for trimming the links in the 
Activities.
*/
public override List<Uri> Trim(List<Uri> uris)
{
List<Uri> resultUris = new List<Uri>();
List<Uri> toBeTrimmedUris = new List<Uri>();
foreach (Uri checkUri in uris)
{
if (IsUrlMatched(checkUri, m_includePaths))
{
toBeTrimmedUris.Add(checkUri);
}
else if (IsUrlMatched(checkUri, m_excludePaths))
{
resultUris.Add(checkUri);
}
/* Original Code- Commented out. By default trim unknown uri */
// else
// {
// toBeTrimmedUris.Add(checkUri);
// }
/*--------- */
/*Modified Code*/
else
{
/* Since these are not internal these need not be trimmed and hence can 
safely be added to the result collection */
resultUris.Add(checkUri);
}
// End of Modified Code
}
// Execute Security Trimming.
resultUris.AddRange(base.Trim(toBeTrimmedUris));
return resultUris;
}
#endregion
//Remaining code from the article…..

Making the above changes is half the solution. The reason is that the OOTB trimmer is not initialized with any paths, and hence if we use the custom trimmer with the changes, it won’t trim any links and would function as if there was no trimmer registered.
The only thing left to do is to register the trimmer in a way so that the paths are initialized as needed.
We can register the trimmer using a feature receiver or create a Central Administration application page. The core code needed to register the trimmer looks like this:

 
int trimmerID = 1; //Default OOTB Trimmer ID is set as 0;
string webApp = “http://yourInternalWebApp”;
NameValueCollection trimmerProperties = new NameValueCollection();
StringBuilder incPaths = new StringBuilder();
//Add as many paths needed separated by the new line character.
incPaths.appendFormat(“{0}{1}”,webApp,Environment.NewLine);
trimmerProperties.Add("IncludePaths", incPaths.ToString());
string serviceLookupName = "Your User Profile Service Application Name";
SPFarm farm = SPFarm.Local;
SPServiceApplicationProxy upaProxy = null;
foreach (SPServiceApplicationProxyGroup servicegroup in
farm.ServiceApplicationProxyGroups)
{
foreach (SPServiceApplicationProxy proxy in servicegroup.DefaultProxies)
{
if (proxy.Name == serviceLookupName)
{
upaProxy = proxy;
break;
}
}
}
if (upaProxy != null)
{
//If you want to see which ones are registered use the following API
Dictionary<int, KeyValuePair<string, NameValueCollection>>
regTrimmers =
PluggableSocialSecurityTrimmerManager.GetRegisteredInfo(upaProxy.Id);
//Un-Register the default trimmer – 0 is the ID of the default trimmer.
PluggableSocialSecurityTrimmerManager.UnregisterPluggableSecurityTrimmer(upaProxy.Id,0);
 
//Register our custom trimmer 
string assmName = typeof(CustomSocialSecurityTrimmer).AssemblyQualifiedName;
PluggableSocialSecurityTrimmerManager.RegisterPluggableSecurityTrimmer(upaProxy.Id,trimmerID,
assmName, trimmerProperties);
}

You can extend this even further by giving the administrators a configuration page where they can add the paths that should be included and excluded and these would be passed on to the trimmer while registration. You could keep that information as a persisted object at the farm level or in a list in one of the sites.
Note: An IIS Reset is needed every time a new list of paths is registered with the trimmer as the paths are cached and won’t be picked up until the IIS has been reset.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Amol Ajgaonkar

More from this Author

Follow Us