0

Following my previous question The folowing code when executed locally as a console app is working fine, But when I include the same code in a WCF service, I am getting Access is denied exception.

using (SPSite newspSite = SPContext.Current.Site)
{
     foreach (SPService service in newspSite.WebApplication.Farm.Services)
     {

             foreach (SPJobDefinition jobDefinition in service.JobDefinitions)
             {

                  if (jobDefinition.Name == "Word Automation Services")
                  {
                         jobDefinition.RunNow();
                         break;
                  }

             }
      }
}

This code is giving problems as it is not possible to run Microsoft.SharePoint.Administration methods remotely. The workarounds for this as mentioned here is only working for SPWebService and is not available for WordService.

Solutions Tried:

a)Tried wrapping the code inside

SPSecurity.RunWithElevatedPrivileges(delegate{ [code] })

But getting the same error.

Screenshot

Manged to debug the service,

enter image description here

Are there any alternatives or workarounds???

12
  • A few questions: is this a claims auth site? does the user have access to the site the service running on? does the service try to use or read the user's identity from SPContext? is the "client" app SharePoint, console, or other? Are you using dynamic configuration? (see MSDN msdn.microsoft.com/en-us/library/ff521581(office.14).aspx) May 3, 2012 at 15:11
  • Its using windows login for authenticating, I am not using spcontext anywhere in the code,Its a web service created using CSKDev wcf service visualstudio template. I am not using any config files.
    – Flowerking
    May 3, 2012 at 15:38
  • 1
    When you wrapped in RWEP did you change SPSite newspSite = SPContext.Current.Site into SPSite newspSite = new SPSite(SPContext.Current.Site.Id)? By the way you should NEVER write using (SPSite newspSite = SPContext.Current.Site) as this will dispose a SPSite you're not in control of. May 4, 2012 at 11:12
  • Ok. I changed it now and tried redeploying, but it is still trowing the same "Access denied" exception. And thanks for the tip on using statement. I am changing my other code accordingly.
    – Flowerking
    May 4, 2012 at 11:58
  • I don't believe that your code works in a console app, since SPContext.Current relies on HttpContext.Current and this is not available in a console app.
    – Matthias
    May 4, 2012 at 12:13

8 Answers 8

2

I had the same issue and got it to work in 2 ways. You have to make the app pool account running the WCF service either an Administrator on the SharePoint Server or make it a Farm Administrator. You also have to give this account rights on the Content Database as well as the SharePoint Config Database. For some reason I am finding out that I also have to give it permissions on the SharePoint_Admin_guid Database.

Anyone know of a way to get this to work with lesser permissions?

Thanks, Sabin

1
  • It is indeed a permission issue, but is it possible to deploy the WCF service without impersonation enabled? I am currently using wcf factory attributes [BasicHttpBindingServiceMetadataExchangeEndpointAttribute] so the only way for me is to make the remote account using the service a farm administrator. It is working for now, but I doubt if it will work when deployed.
    – Flowerking
    Jun 6, 2012 at 16:00
1

I've hit many similar issues working with custom WCF services hosted inside of SharePoint. Troubles seems to multiply when you add in claims auth. I've often reverted to using asmx services. There are known issues with the dynamic configuration of WCF SOAP services inside of SharePoint (noted here). If appropriate you may want to try configuring it as a WCF REST service instead. There is an blog post here where someone hit an issue with similar symptoms and what they did to resolve it.

1
  • +1 Thank you for answering, the links are informative. However, the problem is within my code, please check updated question.
    – Flowerking
    May 4, 2012 at 10:41
1

There are a few workarounds that I can think of.

Option 1) Don't use a WCF service, Use client OM to write a simple "flag" item to a list. Associate a workflow to the flag list which starts when new items are added. The workflow could start the timer job.

Option 2) Code an ItemAdded event hander and do the conversion with the ConversionJob class. Example

3
  • Yes, That is what I thought of doing in the first place, but the requirement is to use Sharepoint as a server to convert docx to pdf or other formats, enabling a wcf service that do the conversion and return the file as a stream. So Is my approach is totally wrong then? This requirement is only part of the whole solution, as the word is also generated dynamically.
    – Flowerking
    May 4, 2012 at 16:02
  • I see. I didn't realize you had to send the file back. Sounds like you need a message queue (or similar mechanism) so that the client can receive notification (or check to see) when the conversion is complete. I think a syncronous call is going to give you nightmares. May 4, 2012 at 17:00
  • Yes, I have to check asynchronously till the conversion is complete.
    – Flowerking
    May 4, 2012 at 17:04
1

I assume your WCF service is hosted SharePoint. Did you used Network Credentials for your Service? Also make sure you are not domains when calling the WCF service.

Please tell me if i'm wrong, but it seems you actually try to force a Word Automation conversion job to run, right? Shouldn't you rather than use something like this (to trigger a specific Conversion Job):

string siteUrl = "http://sp.local";
    string wordAutomationServiceName = "Word Automation Services";
    using (SPSite spSite = new SPSite(siteUrl))
    {
        ConversionJob job = new ConversionJob(wordAutomationServiceName);
        job.UserToken = spSite.UserToken;
        job.Settings.UpdateFields = true;
        job.Settings.OutputFormat = SaveFormat.PDF;
        job.AddFile(siteUrl + "/Shared%20Documents/Test.docx", siteUrl + "/Shared%20Documents/Test.pdf");
        job.Start();
    }
3
  • Yes, its hosted on sharepoint, please check my other question for cofig. and other details. I am using imperosonation with ntlm and its working fine for other operations for the same service. But for the operation containg the above code block giving Access denied error.
    – Flowerking
    May 4, 2012 at 14:28
  • see my edit above! May 4, 2012 at 16:28
  • This is exactly what my code is, but it is adding a conversion job to the job queue. And the conversion is starting when the conversion timer job is scheduled to run. Instead, I am trying to force it using my above code after starting the job as in your code - which is working in a console app but giving problems when called remotely :(
    – Flowerking
    May 4, 2012 at 16:57
1

First a question, did you create the WCF service using CKSDEV, which creates a SharePoint WCF Service Factory based WCF (cleaner, full SharePoint support, no need for a config file), or a "regular" WCF service deployed to the ISAPI folder?

Also, I do see a few problems:

First, To consume a WCF service running in SP, you always need to set the impersonation level in the consuming code:

var client = new YourServiceClient();
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

Second, elevation only works if you actually create a new context inside it,

SPSecurity.RunWithElevatedPrivileges(delegate{
using (SPSite newspSite = SPContext.Current.Site)    
  {
    // your elevated actions (not actually elevated
  }
}); // the current context is disposed!!

does not create an alevated site object, even worse, it disposes the actual current context! Use the following:

SPSecurity.RunWithElevatedPrivileges(delegate{
  using (SPSite newspSite = new SPSite(SPContext.Current.Site.Url))    
  {
    // your elevated actions
  }
});

Last: Anything running at the farm level being called from a webapp will not work, seeing as even under elevated prviliges the code will run as the web app's apppool, which is not a farm admin (I hope :-D).

Call the service under the Central Admin, which makes it run under that site's app pool (the farm account). This does mean the user running the code needs farm level access, so probably not going to work.

In the past i have circumvented this by assigning an account to a web app's root site collection's secondary site admin. This is a service account (so in AD does not have any logon permissions: logon locally, logon as batch / service etc.) This user should be made a member of the farm admin group. Then use the following code to get that user and then run code as that user:

SPSecurity.RunWithElevatedPrivileges(delegate{
  using (SPSite elevatedSite = new SPSite(SPContext.Current.Site.Url))    
  {
    using (SPSite newspSite = new SPSite(elevatedSite.Url, elevatedSite.SecondaryContact.UserToken))    
    {
      // your elevated actions, running as the secondary admin
    }
  }
});

P.S. The doc conversion service runs asynchronously, so after firing the job, you don't know when it actually executes, so you can't for instance download the resulting document immediately after.

3
  • +1 Thank you Colin for your response. as you can see in the config section <behavior name="NewBehavior0"> i configured impersonation correctly,and I am also ok with your second suggestion. I have to check your last suggestion to see if it works.
    – Flowerking
    May 4, 2012 at 17:01
  • And yes, I am using CSKDev template to create the service,and all the config. section is gud as I can use other operations on the service. But only operation with the above code is giving access denied exception.
    – Flowerking
    May 4, 2012 at 17:08
  • Tried everything as suggested, still getting same exception strangely. I thought your last approach will get rid of this for me, but to my surprise nothing is working in my way ..
    – Flowerking
    May 8, 2012 at 11:06
0

Based on some of the other comments, what if you create an additional web service that just starts the timer job on demand? Reference and call this new service (as if it were hosted in the central admin web app) from your existing WCF service. The code that runs inside the new service can run as elevated if need be. I think that should solve the access denied issue.

3
  • 1
    Additionally you can get a Farm reference using SPFarm.Local, you don't have to climb up the object chain from the current site. i.e. SPFarm.Local.Services May 4, 2012 at 17:41
  • Or May be if I create the new service as you suggested as an .asmx service, then it will most likely end this issue, i think. I will give it a try and will update here accordingly.. Thanks a lot for the help!
    – Flowerking
    May 4, 2012 at 17:43
  • ASMX or WCF does not matter, it is the admin code you are trying to run that needs farm admin priviliges (just check the methods you are calling using ILSpy to see the security check in the actual SharePoint code)
    – Colin
    May 4, 2012 at 17:47
0

I've had a similar issue trying to connect to the user profile web service. I could connect to the web service but would get an access denied error every time I invoked it.

In the end it, it turned out the alternate access mappings were not configured correctly for the url I was trying to access the site with. I would double check the alternate access mappings for the url you are trying to connect with.

Sorry if this doesn't help.

0

I am facing a similar issue but the difference is that i have the conversion job code in a web part. It is the action initiated by the user.

I have tried giving the app pool account permission on the word automation services in central admin. I have granted db-owner rights to the sharepoint config database as well. (A quick q here : which rights am i supposed to give?)

Please let me know what is wrong as i have been stuck with it for a long long time now.

Code : string wordAutomationServiceName = "Word Automation Services";

                    ConversionJob job = new ConversionJob(wordAutomationServiceName);
                    job.UserToken = iSPSite.SystemAccount.UserToken;
                    job.Settings.UpdateFields = true;
                    job.Settings.OutputFormat = SaveFormat.Automatic;
                    job.AddFile(web.Url + "/" + Constants.collatedArticlesLib + "/" + lblColDocName.Text,
                        FileToSave);
                    job.Start();

                    foreach (var service in iSPSite.WebApplication.Farm.Services)
                    {
                        if (service.TypeName == "Word Automation Services")
                        {
                            foreach (var jobDefinition in service.JobDefinitions)
                            {
                                if (jobDefinition.Name == "Word Automation Services")
                                {
                                    **jobDefinition.RunNow();**
                                    break;
                                }
                            }
                        }
                    }

                    while (true)
                    {
                        Thread.Sleep(5000);
                        ConversionJobStatus status = new ConversionJobStatus("Word Automation Services", job.JobId, null);
                        if (status.Count == status.Succeeded + status.Failed)
                        {
                            break;
                        }
                    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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