Custom web service in SharePoint 2013 with impersonation

In SharePoint 2010, we used custom web services (NOT SOAP!) To make some third-party data available for JS code on pages displayed by the browser. This was sensitive data, so we used impersonation to ensure that only the right users could access it. Our solution no longer works in SharePoint 2013. Since the original solution is rather complicated, I built a small and simple service in SP 2013 to learn how you can configure a web service with impersonation. The service is deployed to an ISAPI subfolder.

This is an impersonal framework that works:

TestService.svc:

<%@ ServiceHost Language="C#" Debug="true" Service="Sandbox.TestService, $SharePoint.Project.AssemblyFullName$" CodeBehind="TestService.svc.cs" Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 

Code in TestService.svc.cs:

 using Microsoft.SharePoint.Client.Services; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel.Activation; using System.ServiceModel; using System.ServiceModel.Web; namespace Sandbox { [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class TestService { [OperationContract] [WebGet(UriTemplate = "GetAllNumbers", ResponseFormat = WebMessageFormat.Json)] List<int> GetAllNumbers() { List<int> result = new List<int>(); result.AddRange(new[] { 1, 1, 2, 3, 5, 8, 13 }); return result; } } } 

When I execute a GET on http://pc00175/_vti_bin/Sandbox/TestService.svc/GetAllNumbers , I get the expected response [1,1,2,3,5,8,13] . Still. Now I'm trying to use impersonation:

 using Microsoft.SharePoint.Client.Services; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel.Activation; using System.ServiceModel; using System.ServiceModel.Web; using System.Security.Principal; namespace Sandbox { [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class TestService { [OperationContract] [WebGet(UriTemplate = "GetAllNumbers", ResponseFormat = WebMessageFormat.Json)] List<int> GetAllNumbers() { List<int> result = new List<int>(); WindowsImpersonationContext ctx = ServiceSecurityContext.Current.WindowsIdentity.Impersonate(); try { result.AddRange(new[] { 1, 1, 2, 3, 5, 8, 13 }); } finally { ctx.Undo(); } return result; } } } 

Now I get a System.InvalidOperationException message with the message "Anonymous authentication cannot impersonate." when calling ServiceSecurityContext.Current.WindowsIdentity.Impersonate() . I need to tell WCF that we need an impersonation for this call. So I added the [OperationBehavior(Impersonation=ImpersonationOption.Required)] attribute:

 using Microsoft.SharePoint.Client.Services; using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel.Activation; using System.ServiceModel; using System.ServiceModel.Web; using System.Security.Principal; namespace Sandbox { [ServiceContract] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class TestService { [OperationContract] [WebGet(UriTemplate = "GetAllNumbers", ResponseFormat = WebMessageFormat.Json)] [OperationBehavior(Impersonation=ImpersonationOption.Required)] List<int> GetAllNumbers() { List<int> result = new List<int>(); WindowsImpersonationContext ctx = ServiceSecurityContext.Current.WindowsIdentity.Impersonate(); try { result.AddRange(new[] { 1, 1, 2, 3, 5, 8, 13 }); } finally { ctx.Undo(); } return result; } } } 

Now I found the following error in the SharePoint log:

 Error when open web service: System.InvalidOperationException: The contract operation 'GetAllNumbers' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('WebHttpBinding','http://tempuri.org/') for contract ('TestService','http://tempuri.org/'. at System.ServiceModel.Dispatcher.SecurityValidationBehavior.WindowsIdentitySupportRule.ValidateWindowsIdentityCapability(Binding binding, ContractDescription contract, OperationDescription operation) at System.ServiceModel.Dispatcher.SecurityValidationBehavior.WindowsIdentitySupportRule.Validate(ServiceDescription description) at System.ServiceModel.Dispatcher.SecurityValidationBehavior.System.ServiceModel.Description.IServiceBehavior.Validate(ServiceDescriptio... 

Then I guessed that I needed to add web.config next to TestService.svc and add TransportCredentialsOnly mode, but that didn't help:

 <?xml version="1.0"?> <configuration> <system.serviceModel> <bindings> <webHttpBinding> <binding> <security mode="TransportCredentialOnly"> <transport clientCredentialType="Ntlm"/> </security> </binding> </webHttpBinding> </bindings> </system.serviceModel> </configuration> 

I get the same error in the SharePoint log file.

Hope someone has a hint.

Thanks for reading this!

Peter

+5
source share
1 answer

If you want to use REST, why don't you just create an application for this? This is easier than exposing the WCF service in SharePoint. Then you can configure your own security settings.

This article should help you with this.

0
source

Source: https://habr.com/ru/post/1205429/


All Articles