0

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.

  • Are you developing this as an AppPart from an App or as a Webpart from a Full-Trust solution? – user2536 Aug 4 '15 at 7:07
  • Full-trust solution. – Conium Aug 4 '15 at 8:18
1

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;
  • The web part works out the query fine. This was very informative. However even when the site collection is configured to anonymous access to entire web site, if I were to add this web part any place in a page. It forcefully asks for an authentication. If i removed the web part. Anonymous can now access the site collection. – Conium Aug 4 '15 at 9:23
  • Oops my bad, once the removing the usertoken, the number of results came back similarly as the system account's of 5. I will now proceed towards extracting the managedproperties and such. – Conium Aug 4 '15 at 9:51
0

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.

  • Yeah initially I thought of that and questioned the task with the same thought and I think it is possible to edit a page and in turn it will find the relevant page in the public site and update the summary content. But this anonymous content "summaries" takes up space, so if I had tons of pages and items it would post a problem. In my case some pages and items from the Intranet side will be available to the public site. – Conium Aug 5 '15 at 2:31
  • We did it with workflow and just used a list for the summaries, not pages. The list was "Premium Content Summaries". It worked well. – Matthew McDermott Aug 6 '15 at 11:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.