none
Permissions for app via a web API RRS feed

  • Question

  • Hi,

    I have a web API written in C# ASP.NET core 2.1. It runs through IIS and has a number of end points for client connections to pass data into, to create records in their Sage Accpac accounting system. The end points all accept XML documents in the request header. One of the end points allows for the client to pass in cheque (check for you in the US) payment records into Sage. The way this works is that the XML document they pass in is deserialised into an object and I pull out the relevant data I need and construct a call into Sage and post the record. That bit works just fine. There are limitations to the way the web API works and to get around them, I have written two external applications that run by being passed command line parameters, and the web API executes the application which returns statuses via its return code. The first application that is called will cause the cheque to be printed out on a given printer. If I run the application in standalone mode on a record that is in Sage, it will print the cheque on any printer I tell it to. The printer name and port are passed in as command line parameters. If I let the web API make the call to run the printing program, it does not select the printer correctly. This appears to be a quirk in the way Sage deals with selecting printers, so to get around this I added code that changes the default printer to the chosen one each time, which is fine, and again works when I run the program manually. It doesn't however, change the default printer when the application is called from the web API. I know the command line parameters it is passed are correct, as the web API logs the arguments out to a database. The cheque will print out, but not on the chosen printer because the changing of the default printer hasn't happened when it is called from the web API. This has a feel of permissions about it.

    The web API runs in an app pool under a given user name in Windows. The cheque printing application is set to run as administrator, but I think that when it is running after being called from the web API that it picks its permissions up from the calling API rather than what has been set in Windows. Any ideas? I created a class to allow me to set the default printer.

        public static class WindowsPrinters
        {
            [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool SetDefaultPrinter(string Name);
        }

    Once the parameters have been picked up, I then make a call to that function to set it like this:

            private void SetDefaultPrinterForSage(string psPrinterName)
            {
                WriteLog("Setting default printer to: " + psPrinterName);
                WindowsPrinters.WindowsPrinters.SetDefaultPrinter(psPrinterName);
            }
    Wednesday, March 27, 2019 9:13 AM

All replies

  • The web API runs in an app pool under a given user name in Windows. The cheque printing application is set to run as administrator, but I think that when it is running after being called from the web API that it picks its permissions up from the calling API rather than what has been set in Windows. Any ideas? I created a class to allow me to set the default printer.

    All ASP.NET Web programs run under the context of the WPW3.exe program that services a given Application Pool. 

    https://stackify.com/w3wp-exe-iis-worker-process/

    All of your issues should be addressed at the ASP.NET forums for ASP.NET Core and WebAPI, along with whatever it is you're trying to do at the Windows desktop.

    https://forums.asp.net/

    Wednesday, March 27, 2019 9:29 AM
  • This is about the Winforms application and not the web API. The application is written as a standard Windows Forms application in Visual Studio 2017. I only mentioned the web API for background.

    I added additional code to the application to check what user context it is running under. It is running as an administrator. In the app.manifest I set the <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

    In the code, I added a function that tests the admin level of a user and the function I put in is:

            public bool IsUserAdministrator()
            {
                try
                {
                    WindowsIdentity user = WindowsIdentity.GetCurrent();
                    WindowsPrincipal principal = new WindowsPrincipal(user);
                    return principal.IsInRole(WindowsBuiltInRole.Administrator);
                }
                catch
                {
                    return false;
                }
            }

    I then log out the return from this function and regardless of whether I run it from within VS, from the command line or let it get called via the web API, it is returning true, so it is running as administrator. However, the function that sets the printer I modified as SetDefaultPrinter returns a bool.

            private void SetDefaultPrinterForSage(string psPrinterName)
            {
                bool bPrinter = false;
    
                WriteLog("Setting default printer to: " + psPrinterName);
                bPrinter = WindowsPrinters.WindowsPrinters.SetDefaultPrinter(psPrinterName);
    
                WriteLog("bPrinter return: " + bPrinter.ToString());
            }
    Running it from within VS it will change the default printer. If I run it from the command line it will change the printer. If it is called from the web API it does not change the printer despite running as an administrator.

    Wednesday, March 27, 2019 11:49 AM
  • When a child process is started in Windows it inherits the privileges of the parent process unless you use a version of CreateProcess that runs as a different user (of which .NET does not expose).

    You said the cheque app is set to run as admin but how did you do that? Did you add an appmanifest to the application? Did you open the properties for the exe itself and change it there? None of this is going to work when called from a web app. The web app will try to create the process but either of these approaches will trigger a consent prompt if UAC is enabled. Since the web app runs as non-interactive you'd never see the prompt. You cannot run an "admin" level app from your web app. That would be a pretty big security hole in my opinion.

    The approach you've selected itself doesn't seem maintainable to me so I question the approach. You have an API that needs to print something on a printer. Is the printer available to all users on the machine? If so then your API should be able to print it directly. There shouldn't be any issues with printing to a non-default printer. Maybe this is where you said your third party system was having issues but I don't know how your API relates to the third party system such that you printing something is impacted by it.

    I would probably lean toward having a backend process handle the printing in batch mode. This could either be a windows service (if you wanted it running constantly) or a scheduled task (that runs however frequently you want with a limit of 1 minute). In either case the app would run in whatever context you configured it for and would be independent of the API. In this case the API should return a "pending" status to callers when asked to print something, you'd store the print request in a database or something and the backend process would pick it up. After the process printed the data then it could update the database. The API could be polled by interested parties to check on the state of the printing and when done get back anything you need from the result.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, March 27, 2019 2:24 PM
    Moderator
  • When a child process is started in Windows it inherits the privileges of the parent process unless you use a version of CreateProcess that runs as a different user (of which .NET does not expose).

    You said the cheque app is set to run as admin but how did you do that? Did you add an appmanifest to the application? Did you open the properties for the exe itself and change it there? None of this is going to work when called from a web app. The web app will try to create the process but either of these approaches will trigger a consent prompt if UAC is enabled. Since the web app runs as non-interactive you'd never see the prompt. You cannot run an "admin" level app from your web app. That would be a pretty big security hole in my opinion.



    I added an embedded app.manifest and set the requestedExecutionLevel level to be requireAdministrator. My app shows that it runs as an administrtor. The printers are set up on a hosted server and they are redirected to the offices of each requesting user. So any user around the world, that has access will send the request, and it will come out on the printer next to them. UAC is disabled on the server. No users will have access to the server.

    The approach you've selected itself doesn't seem maintainable to me so I question the approach. You have an API that needs to print something on a printer. Is the printer available to all users on the machine? If so then your API should be able to print it directly. There shouldn't be any issues with printing to a non-default printer. Maybe this is where you said your third party system was having issues but I don't know how your API relates to the third party system such that you printing something is impacted by it.

    I would probably lean toward having a backend process handle the printing in batch mode. This could either be a windows service (if you wanted it running constantly) or a scheduled task (that runs however frequently you want with a limit of 1 minute). In either case the app would run in whatever context you configured it for and would be independent of the API. In this case the API should return a "pending" status to callers when asked to print something, you'd store the print request in a database or something and the backend process would pick it up. After the process printed the data then it could update the database. The API could be polled by interested parties to check on the state of the printing and when done get back anything you need from the result.


    Michael Taylor http://www.michaeltaylorp3.net

    I can't create a service to do the print as the requirement is "immediate cheque printing" and the web API needs to get a response and respond to the request to say what it's response is and it includes the cheque amount, cheque date, cheque number, etc. All of these cannot be created from Sage without having called Sage's print function.

    FYI, the API is restricted by IPs to the offices that are allowed to access it and there is sufficient security preventing anything getting to the printer processor because the web API has the job of putting the details into Sage, and the print processor is executed only with a batch number and printer destination which has to be set up in Windows by our tech support guys. If the printer processor doesn't find the batch, it will simply exit. Suffice to say, that this approach is about the only approach I have as the whole thing must be inside the same request/response cycle in the web API.

    So, I redid the application to clean it up, and it runs within the correct context. It is now printing to my test printer. I think I have ruled out anything wrong in my code now. I think the issue lies within Sage and how it caches chosen printers. I will update this post as I work through it.

    Thanks for the input. Always appreciated.

    Wednesday, March 27, 2019 3:03 PM
  • Hi

    Is your problem solved? If so, please post "Mark as answer" to the appropriate answer, so that it will help other members to find the solution quickly if they face a similar issue.

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, April 2, 2019 2:57 AM
    Moderator