Question

I need to create a search web part with Visual Studio for SharePoint 2013 On-Prem that enables anonymous users to search for Items and People which they may have no accessed to (Example: The List is not set to anonymous accessible).

This scenario is needed as some information within the Item should not be shown to the public. Hence I would like to obtain all public information to be shown within the Web Part.

I have tried using CoreResultsSearchWebPart with elevated privileges http://kjellsj.blogspot.sg/2012/04/elevated-search-results-sharepoint-2010.html

However, this CoreResultsSearchWebPart will not allow searching of people(As People search has a PeopleCoreResultsWebPart) or a text box for user search(I could not find a connection to send query to the CoreResultsSearchWebPart)

Do I really need to create a search webpart from scratch? If so could anybody give me some examples? I have tried using SharePoint rest API of Client Object Model but it does not work.

Here is the sample code of mine

using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Data;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Search;
using Microsoft.SharePoint.Client.Search.Query;
namespace CustomSearchWebPartTrial.CustomSearchWebPart
{
    [ToolboxItemAttribute(false)]
    public partial class CustomSearchWebPart : WebPart
    {
        // Uncomment the following SecurityPermission attribute only when doing Performance Profiling on a farm solution
        // using the Instrumentation method, and then remove the SecurityPermission attribute when the code is ready
        // for production. Because the SecurityPermission attribute bypasses the security check for callers of
        // your constructor, it's not recommended for production purposes.
         [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Assert, UnmanagedCode = true)]

        public CustomSearchWebPart()
        {
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
        }
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();
        }

        protected void Page_Load(object sender, EventArgs e)
        {

            SPSite site = SPContext.Current.Site;
            using (ClientContext clientContext = new ClientContext(site.Url))
            {
                KeywordQuery keywordQuery = new KeywordQuery(clientContext);

                keywordQuery.QueryText = "SPContentType:\"Custom Announcement\"";
                keywordQuery.ClientType = "CSOM";

                SearchExecutor searchExecutor = new SearchExecutor(clientContext);

                ClientResult<ResultTableCollection> results = searchExecutor.ExecuteQuery(keywordQuery);
                clientContext.ExecuteQuery();
                int count = 0;
                foreach (var resultRow in results.Value[0].ResultRows)
                {
                    count++;
                }
                lblCount.Text = count.ToString();//checking for number of items. Example of this query for my authenticate users is 5, it should return 5 for anonymous as well.
            }
        }
    }
}

I got errors of IgnoreSafeQueryPropertiesTemplateUrl = true as my users and anonymous users don’t have the UseRemoteAPIs() permission.

So i set within the code to IgnoreSafeQueryPropertiesTemplateUrl = false, which brings up another error where it mentions

Microsoft.Office.Server.Search.REST.SearchServiceException: The SafeQueryPropertiesTemplateUrl "The SafeQueryPropertiesTemplateUrl "{0}" is not a valid URL." is not a valid URL. And Followed the guide of http://blog.mastykarz.nl/configuring-sharepoint-2013-search-rest-api-anonymous-users/

 protected void Page_Load(object sender, EventArgs e)
        {

            SPSite site = SPContext.Current.Site;
            using (ClientContext clientContext = new ClientContext(site.Url))
            {
                KeywordQuery keywordQuery = new KeywordQuery(clientContext);
                keywordQuery.IgnoreSafeQueryPropertiesTemplateUrl = false;
                keywordQuery.QueryText = "SPContentType:\"Custom Announcement\"" + "&QueryTemplatePropertiesUrl=\"spfile://webroot/queryparametertemplate.xml\"";

                keywordQuery.ClientType = "CSOM";

                SearchExecutor searchExecutor = new SearchExecutor(clientContext);

                ClientResult<ResultTableCollection> results = searchExecutor.ExecuteQuery(keywordQuery);
                clientContext.ExecuteQuery();
                int count = 0;
                foreach (var resultRow in results.Value[0].ResultRows)
                {
                    count++;
                }
                lblCount.Text = count.ToString();
            }

But it still failed.

Was it helpful?

Solution

I see that you are using ClientContext which is ment to be used when developing Apps for SharePoint, as Apps can't use the SharePoint API.

Since it's a full-trust solution you are developing, use the SharePoint API.

In your PageLoad method, use SPSecurity.RunWithElevatedPrivileges() to elevate the permissions of the current user, anonymous or not, and execute the Search code.

Also, since you are going to elevate permissions for users who don't have access to lists and libraries, you have to change the web context.

Here's a snippet:

// Store the current site ID in a variable, you'll need it
Guid siteId = SPContext.Current.Site.ID;

// Get the user token for the System Account
// The System Account should automatically have Site Collection permissions
SPUserToken systemAccountUserToken = SPContext.Current.Site.SystemAccount.UserToken;

// And store the current web context in a variable as well.
// As I mentioned before, you'll have to change it, but also change it back again
HttpContext currentUserContext = HttpContext.Current;

// Clear the current context
if (SPContext.Current != null)
    HttpContext.Current = null;

// Elevate privileges
SPSecurity.RunWithElevatedPrivileges(() =>
{
    // Impersonate the System Account
    using (SPSite elevatedSite = new SPSite(siteId, systemAccountUserToken))
    {
        // Here begins the magic
        // Creating a new Web Context
        HttpRequest httpRequest = new HttpRequest(string.Empty, elevatedSite.RootWeb.Url, string.Empty);
        HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new StringWriter()));
        // And here we set the new Web Context. Muy importante
        SPControl.SetContextWeb(HttpContext.Current, elevatedSite.RootWeb);

        // By this point user should be running as the System Accounts
        // and be able to execute code with the right level of privileges
        KeywordQuery keywordQuery = new KeywordQuery(elevatedSite);
        keywordQuery.QueryText = "SPContentType:\"Custom Announcement\"" + "&QueryTemplatePropertiesUrl=\"spfile://webroot/queryparametertemplate.xml\""
        SearchExecutor searchExecutor = new SearchExecutor();
        ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(keywordQuery);
        var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);
        resultTable = resultTables.FirstOrDefault();
    }
});

// Remember I said change back the Web Context? Here we go:
HttpContext.Current = currentUserContext;

OTHER TIPS

Just a thought, but rather than follow this course of action and gut security with a full trust web part. Why not create anonymous content "summaries" that users have access to and just return those results? You can customize the result so that it links to the original secure content. I have don't this for several clients and it works very well with zero customization.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top