Navision 2009 SP1 Web Services / RTC issues

Robert_EllsworthRobert_Ellsworth Member Posts: 19
edited 2015-08-27 in NAV Three Tier
Hello everyone,

I'm trying to setup a 3-tier installation following the Walkthough provided by Microsoft: ( https://msdn.microsoft.com/en-us/library/dd301254.aspx )

I have 2 machines currently setup. First is the SQL Server (omisqldev), and the second machine is the Web Services machine (ws1). Both machines are members of the EOMEGA domain, and I created 1 Domain User for use for both system's services (SQLAdmin).

Here are the services for the Web Services server:
NAvService.png

Here are the services for the SQL Server:
SQLservice.png

Here is the Domain User for both services:
DomainUser.png

Here is the list of delegations, reported by setspn:
setspn_list.jpg

And this is the output of the Best Practices Analyser:
BestPractice.jpg

Unless I'm missing something, the MSSQLSvc/omisqldev.eomega.org SPN is defined for the service account user. I don't know what's missing in the setup.

When I try to connect from a third machine via Web Services, I am presented with a login screen:
WS1_Auth.jpg

After providing the credentials for a local account to WS1 (Domain Users didn't work) I get:
WS1_LogonFail.jpg

Anyone have any suggestions on where I should go next or what the missing piece to this puzzle is?

Thanks for taking the time to read this,

- Rob

Comments

  • yukonyukon Member Posts: 361
    Hi,

    Do you set "WebServicesUseNTLMAuthentication = true" under service config?


    Regards,
    Yukon
    Make Simple & Easy
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    Yes, I have tried it with WebServicesUseNTLMAuthentication = true, and false. Neither setting made a difference :(
  • yukonyukon Member Posts: 361
    Hi,

    How about the RTC? Can you open the nav with RTC? What is the database role membership for SQLAdmin on your database. If you still face with same error, try to use separate a/c and re-configure again.


    Regards,
    Yukon
    Make Simple & Easy
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    Hello,

    The Windows users have been added to the database. I can connect using the Classic client in Windows Authentication mode for the SQLAdmin user.

    Also, I am able to run the RTC, but only on WS1 (when logged into the domain with SQLAdmin).

    It looks to be a delegation issue, as the SQL Logs show attempts of logging in as Anonoymous

    Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'. Reason: Token-based server access validation failed with an infrastructure error. Check for previous errors. [CLIENT: 192.168.0.51]

    The infrastructure error is the missing SPN that IS setup (at least what I believe to be setup, as provided by the setspn screen capture I provided). The BPA says the SPN isn't setup, when it looks like it is.

    Thank you for your answers so far Yukon,

    - Rob
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    It was the "WebServicesUseNTLMAuthentication" change to true afterall.

    I figured out where my error was in this whole process.

    After I had set (on the Navision Service) the "WebServicesUseNTLMAuthentication" to true, I never tested logging into Web Services with the Domain User. I was able to log in to Web Services with the Domain User and I got the expected results from the session:
    This XML file does not appear to have any style information associated with it. The document tree is shown below.
    
    <discovery xmlns="schemas.xmlsoap.org/.../" xmlns:xsi="www.w3.org/.../XMLSchema-instance" xmlns:xsd="www.w3.org/.../XMLSchema">
    
    <contractRef xmlns="schemas.xmlsoap.org/.../" ref="192.168.0.51/.../Omega Testing 2014/SystemService"/>
    
    <contractRef xmlns="schemas.xmlsoap.org/.../" ref="192.168.0.51/.../Omega Testing 2014/Page/Contact"/>
    
    <contractRef xmlns="schemas.xmlsoap.org/.../" ref="192.168.0.51/.../Omega Testing 2014/Page/Customer"/>
    
    <contractRef xmlns="schemas.xmlsoap.org/.../" ref="192.168.0.51/.../Omega Testing 2014/Page/RoomCategory"/>
    
    </discovery>
    

    Thank you for your assistance Yukon!
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    Hello everyone, I'm now at a point where I am trying to access a codeunit from PHP. I have the codeunit registered with WS, and I can pull up the WDSL for it:
    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="urn:microsoft-dynamics-schemas/codeunit/GuestInfo" targetNamespace="urn:microsoft-dynamics-schemas/codeunit/GuestInfo">
    <types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="urn:microsoft-dynamics-nav/xmlports/GetGI" elementFormDefault="qualified" targetNamespace="urn:microsoft-dynamics-nav/xmlports/GetGI">
    <complexType name="ContactHeader">
    <sequence>
    <element minOccurs="1" maxOccurs="unbounded" name="ContNoTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContNameTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContName2Title" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContAddressTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContAddress2Title" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContAddress3Title" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContCountyTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContPostCodeTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContCityTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContCountryRegionCodeTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContE-MailTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContFirstNameTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContMiddleNameTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContSurnameTitle" type="string"/>
    <element minOccurs="1" maxOccurs="unbounded" name="ContE-ActiveTitle" type="string"/>
    </sequence>
    </complexType>
    <complexType name="Contact">
    <sequence>
    <element minOccurs="1" maxOccurs="1" name="No" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="Name" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="Name2" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="Address" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="Address2" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="Address3" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="County" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="PostCode" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="City" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="CountryRegionCode" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="EMail" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="FirstName" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="MiddleName" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="SurName" type="string"/>
    <element minOccurs="1" maxOccurs="1" name="EActive" type="string"/>
    </sequence>
    </complexType>
    <complexType name="Root" mixed="true">
    <sequence>
    <element minOccurs="1" maxOccurs="unbounded" name="ContactHeader" type="tns:ContactHeader"/>
    <element minOccurs="1" maxOccurs="unbounded" name="Contact" type="tns:Contact"/>
    </sequence>
    </complexType>
    <element name="Root" type="tns:Root"/>
    </schema>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:microsoft-dynamics-schemas/codeunit/GuestInfo">
    <element name="GetGuest">
    <complexType>
    <sequence>
    <element minOccurs="1" maxOccurs="1" name="guestNo" type="string"/>
    <element xmlns:q1="urn:microsoft-dynamics-nav/xmlports/GetGI" minOccurs="1" maxOccurs="1" name="gIXML" type="q1:Root"/>
    </sequence>
    </complexType>
    </element>
    <element name="GetGuest_Result">
    <complexType>
    <sequence>
    <element minOccurs="1" maxOccurs="1" name="return_value" type="int"/>
    <element xmlns:q2="urn:microsoft-dynamics-nav/xmlports/GetGI" minOccurs="1" maxOccurs="1" name="gIXML" type="q2:Root"/>
    </sequence>
    </complexType>
    </element>
    </schema>
    </types>
    <message name="GetGuest">
    <part name="parameters" element="tns:GetGuest"/>
    </message>
    <message name="GetGuest_Result">
    <part name="parameters" element="tns:GetGuest_Result"/>
    </message>
    <portType name="GuestInfo_Port">
    <operation name="GetGuest">
    <input name="GetGuest" message="tns:GetGuest"/>
    <output name="GetGuest_Result" message="tns:GetGuest_Result"/>
    </operation>
    </portType>
    <binding name="GuestInfo_Binding" type="tns:GuestInfo_Port">
    <binding xmlns="http://schemas.xmlsoap.org/wsdl/soap/" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="GetGuest">
    <operation xmlns="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="urn:microsoft-dynamics-schemas/codeunit/GuestInfo:GetGuest" style="document"/>
    <input name="GetGuest">
    <body xmlns="http://schemas.xmlsoap.org/wsdl/soap/" use="literal"/>
    </input>
    <output name="GetGuest_Result">
    <body xmlns="http://schemas.xmlsoap.org/wsdl/soap/" use="literal"/>
    </output>
    </operation>
    </binding>
    <service name="GuestInfo">
    <port name="GuestInfo_Port" binding="tns:GuestInfo_Binding">
    <address xmlns="http://schemas.xmlsoap.org/wsdl/soap/" location="http://192.168.0.51:7047/DynamicsNAV/ws/Omega%20Testing%202014/Codeunit/GuestInfo"/>
    </port>
    </service>
    </definitions>
    

    The function in the codeunit is called GetGuest and it has 2 parameters. First is the GuestNo, which is the value linked to a Contact Record, and the second parameter is GIXML, which is an VAR declared XMLPort to the Contact table. I am trying to access this codeunit via PHP, and am having no luck in getting the XML back from the function.

    Using both Freddy.DK's blogs, as well as Arte's blog post regarding NAV and PHP, I am able to do the basics like accessing the Company List and reading information via a page. When I try to access the codeunit though, I am getting a SOAP Exception: ERROR: SoapException: [SoapFault exception: [a:System.Xml.XmlException] Data at the root level is invalid. Line 1, position 1. in /opt/lampp/htdocs/guest.php:18

    Here's the code in my guest.php file:
    <?
    include('nav_soap_client.php');
    
    stream_wrapper_unregister('http');
    // we register the new HTTP wrapper
    stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
    
    // Initialize Soap Client
    $baseURL = 'http://192.168.0.51:7047/DynamicsNAV/ws/Omega Testing 2014/';
    
    $client = new NTLMSoapClient($baseURL.'Codeunit/GuestInfo');
    $params = array();
    $params["guestNo"] = "CT0061822";
    $params["gIXML"] = null;
    
    stream_wrapper_restore('http');
    try {
        $result = $client->GetGuest($params);
        if ($result->return_value == 1) {
          $ret = TRUE;
        }
    } catch(Exception $e){
         // here should be some nice debugging if you want
               echo "<hr><b>F`in ERROR: SoapException:</b> [".$e."]<hr>";
               echo "<pre>".htmlentities(print_r($client->__getLastRequest(),1))."</pre>";
      }
    
    $xmldata = $result->gIXML;
    $contno = $xmldata->No;
    $fname = $xmldata->FirstName;
    $mname = $result->MiddleName;
    $lnane = $result->Surname;
    $email = $result->Email;
    $emactive = $result->EActive;
    
    echo "Contact No.:".$contno." Name:".$fname." ".$mname." ".$lname;
    ?>
    

    Basically, I'm not getting anything back from the codeunit. The XMLPort does work, as I have written a test codeunit to generate an XML file with it.

    Here's what my registered codeunit looks like:
    OBJECT Codeunit 50090 Test Guest Info
    {
      OBJECT-PROPERTIES
      {
        Date=08/14/15;
        Time=12:09:16 PM;
        Modified=Yes;
        Version List=;
      }
      PROPERTIES
      {
        OnRun=BEGIN
              END;
    
      }
      CODE
      {
    
        PROCEDURE GetGuest@1000000000(GuestNo@1000000000 : Code[20];VAR GIXML@1000000003 : XMLport 50000) : Integer;
        VAR
          Cont@1000000001 : Record 5050;
          GIX@1000000002 : XMLport 50000;
        BEGIN
          IF NOT Cont.GET(GuestNo) THEN
            EXIT(-1);
    
          Cont.SETRANGE("No.", GuestNo);
          GIXML.SETTABLEVIEW(Cont);
        END;
    
        BEGIN
        END.
      }
    }
    

    Any assistance here would be greatly appreciated.

    Thanks for taking the time to read this.
  • Remco_ReinkingRemco_Reinking Member Posts: 74
    Hi Robert,

    First, if you have error checks in functions that are called using webservices, you should not use the exit, but an error instead.
    It appears that the XMLPort is called anyway, no matter if you Exit the function. You can only prevent this from happening with an error.

    Second, the error message you get "Data at the root level is invalid" tells something about the XML returned through the XMLPort. Did you check all properties of your XMLPort?

    for example:

    Encoding : UTF-8
    XMLVersionNo : 1.1
    Format/Evaluate : XML Format/Evaluate

    regards, Remco
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    Hi Robert,

    First, if you have error checks in functions that are called using webservices, you should not use the exit, but an error instead.
    It appears that the XMLPort is called anyway, no matter if you Exit the function. You can only prevent this from happening with an error.

    Second, the error message you get "Data at the root level is invalid" tells something about the XML returned through the XMLPort. Did you check all properties of your XMLPort?

    for example:

    Encoding : UTF-8
    XMLVersionNo : 1.1
    Format/Evaluate : XML Format/Evaluate

    regards, Remco

    Hello Remco,

    Thanks for the heads-up on Exiting a function when XMLPorts are involved. Makes sense. I will change those to error calls instead of exits.

    I have tried all of those settings for the XMLPort (The Format/Evaluate, version, etc.) and the PHP code still doesn't work.

    What the function is supposed to do is return data via the XMLPort. Even though I have the XMLPort setup as Export-Only, I fear the codeunit is trying to execute it as an Import (Since I am feeding the function a null object, and not explicitly calling the XMLPort with XMLPort.EXPORT().

    I can get the data out as a Page, but the pages are behaving differently than the XMLPort. When I extract the data via a page, if a field has no value in the table being accessed it is omitted from the output from the page entirely. In the PHP code I have to test for the existence of the element via isset() for every field which is annoying.

    Ideally I'd like the codeunit to work but I'm not having any luck.

    Thank you for the suggestions though. Appreciate the help :)

    - Rob
  • Remco_ReinkingRemco_Reinking Member Posts: 74
    Hi Rob,
    It should be no problem to call the EXPORT in the function. And you can use it to check if it succeeded:

    e.g. this is a function that also gets called through a webservice using php :
    PROCEDURE GetCustomerDetails(LoginID : Integer;SellToCustomerNo : Code[20];VAR CustomerDetails : XMLport "Customer Details") Result : Text[100]
    IF NOT CheckLoginID( LoginID, 'GetCustomerDetails') THEN
      ERROR('ERROR:Invalid loginID');
    Result := CheckCustomer( SellToCustomerNo, Customer);
    IF Result = 'OK' THEN
    BEGIN
      Customer.SETRECFILTER;
      CustomerDetails.SETTABLEVIEW( Customer);
      IF NOT CustomerDetails.EXPORT THEN
      BEGIN
        Result := 'ERROR:'+COPYSTR(GETLASTERRORTEXT,1,93);
        ERROR( Result)
      END;
    END
    ELSE
      ERROR(Result);
    

    Just another question: Is the first node in your XMLPort a text element?

    Remco
  • Robert_EllsworthRobert_Ellsworth Member Posts: 19
    Here's the codeunit now. I took out the need for using EXIT and if something does go wrong I throw errors:
    GetGuest(GuestNo : Code[20];VAR GIXML : XMLport "Guest XML")
    IF NOT Cont.GET(GuestNo) THEN
      ERROR('-1');
      
    Cont.RESET;
    Cont.SETRANGE("No.", GuestNo);
    GIXML.SETTABLEVIEW(Cont);
    IF NOT GIXML.EXPORT THEN
      ERROR('ERROR:'+COPYSTR(GETLASTERRORTEXT,1,93));
    

    The first node in my XMLPort is a Text Field (Root);
    XMLPort.png

    I added in code to manually do the EXPORT, and it seems to complete but is still not returning anything to the php object. I have written a test codeunit to see if I can generate an xml file using the XMLPort, and it works so I'm a tad confused as to what's wrong.
Sign In or Register to comment.