Usage Scenarios for SQL Server 2005 Native Web Services

 

Srik Raghavan
Microsoft Corporation

Applies to:
   SQL Server 2005
   Web services

May 2005

Summary: Get a detailed look at how to set up SQL Server 2005 for Web service access in a heterogeneous environment, and learn more about key scenarios for Web services in SQL Server 2005. (11 printed pages)

Download the associated WebServicePerlScript.exe code sample.

Contents

Introduction
Heterogeneous Access
Perl Scripts for Administration and Monitoring
Additional Scenarios
Conclusion

Introduction

In SQL Server 2005, we added support for native XML Web services to the database engine. This feature is designed around well-known standards such as SOAP 1.2, WSDL 1.1, and HTTP. Basing the solution on standards enables interoperability and outreach in the heterogeneous environments common to most enterprises.

The new infrastructure added to SQL Server 2005 greatly facilitates exposing Web services directly out of the server, in that having the native SOAP stack built into the database engine obviates the need for a middle-tier process (such as IIS) for this purpose. It also enables SQL Server to participate as a component in service-oriented architectures, as services provide the glue in these new architectures. Native XML Web services allow you to both expose stored procedures as Web services and execute ad hoc T-SQL statements against the database server. In essence we have created a new access mechanism to SQL Server based on SOAP that provides much the same functionality currently available via the Tabular Data Stream (TDS) proprietary binary protocol.

We begin with a detailed look at how to set up SQL Server 2005 for Web service access in a heterogeneous environment. We will see how to use Perl scripts for database administration, and look briefly at additional scenarios where native Web services can be used.

Heterogeneous Access

Consider an environment where applications running on non-Microsoft operating systems need to connect to SQL Server. Our recommendation for such applications is to use SQL Server authorization (SQL-Auth) to connect to a SQL Server 2005 Web service. Let's take a look at how that works.

The first thing the user would need to do to expose a Web service is to create an endpoint. Look at the Data Definition Language (DDL) statement for creating an endpoint, shown below. It exposes a stored procedure named "GetCustomerInfo" as a Web service.

**Note   **The term WEBMETHOD, though conceptually the same as [WebMethod] in ASP.NET, is otherwise unrelated to ASP.NET.

CREATE  ENDPOINT sql_auth_endpoint
STATE = STARTED
AS HTTP (
   SITE = '*', 
   PATH = '/sql/sql_auth', 
   AUTHENTICATION = (BASIC),
   PORTS=(SSL)   
)    
FOR SOAP (
   WEBMETHOD 'GetCustomerInfo' 
(
            name='AdventureWorks.dbo.GetCustomerInfo', 
            schema=STANDARD
) ,
   LOGIN_TYPE = MIXED,
   WSDL = DEFAULT,
   DATABASE = 'AdventureWorks',
   BATCHES=ENABLED,
   NAMESPACE = 'http://Adventure-Works/Customers/'
 ) 

In keeping with the "Secure By Design" theme in SQL Server 2005, we do not allow ANONYMOUS access to SQL Server under any circumstances. This means that all connections need to authenticate at the HTTP transport level using one of the supported authentication schemes. BASIC is one of the most common and widely used authentication models, as it is supported by most clients. However, it is also the least secure option, as it requires the passwords to be sent over in clear text. To avoid this problem, we require that the endpoint be enabled for SSL whenever BASIC is chosen as the authentication type. To enable SSL, one would have to execute the following command,

httpcfg set ssl /i IP:Port /h Hash /g Guid

where the Hash is the certificate hash and Guid is a globally unique identifier (GUID) string identifying the entity registering the certificate. The user can obtain the hash value for the certificate by looking up the Thumbprint value in the Certificate. As a best practice, create a single GUID for each instance of SQL Server and use that same GUID for all certificate registrations made by that instance. You can use any tool to come up with this GUID value. Httpcfg.exe ships with Windows support tools.

So in this case it would become:

httpcfg set ssl /i 1.1.1.1:443 /h 4463b7899c499a38812a7bbe7d73f4d31d026b2f /g "{2bb50d9c-7f6a-4d6f-873d-5aee7fb43290}"

where 1.1.1.1 would be replaced with the IP address of the machine hosting SQL Server.

So how does one enable SQL-Auth on an endpoint? This is done by specifying "LOGIN_TYPE=MIXED" in the payload section of the endpoint syntax. By specifying "MIXED", one can authenticate with the SQL Server 2005 Instance using either integrated or SQL authentication. We now enable the SQL credentials to flow as part of the payload (message). In doing so, we have taken care to ensure that the SOAP header that transports the credentials matches the WS-Security Username token. Adhering to the WS-Security standard inherently promotes interoperability; for example, the username token SOAP header can be generated using the Web Services Enhancements 2.0 for Microsoft .NET (WSE) using very few lines of code.

As you can see in the discussion above, there are two levels of authentication:

  • Transport level
  • Message level

Let us now drill down into how the two levels of authentication work.

All requests are always authenticated at the transport level. So if a user submits invalid BASIC authentication credentials, the connection fails with a HTTP 401 access denied error. If the user authenticates successfully at the transport level, then we have two options. We can use either the transport credentials or the credentials that came as part of the SOAP message to login to SQL Server. The credentials selected are determined by the presence of SQL-Auth credentials in the SOAP message. If credentials are present in the SOAP message, we will attempt to login to the SQL Server database using the SQL-Auth credentials. If this fails, we return a failure to the user, and we do not fall back to using the BASIC authentication credentials. If there are no credentials in the SOAP message, we will attempt to login to the SQL Server using the transport credentials.

The SOAP message containing the SQL credentials appears as follows:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Header>
    <Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-
     200401-wss-wssecurity-secext-1.0.xsd" xmlns="http://docs.oasis-
     open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken>
        <wsse:Username>user</wsse:Username>
        <wsse:Password Type="http://docs.oasis-
         open.org/wss/2004/01/oasis-200401-wss-username-token-profile-
         1.0#PasswordText">
              password
        </wsse:Password>
      </wsse:UsernameToken>
    </Security>
  </soap:Header>
  <soap:Body>
    <GetCustomerInfo xmlns="http://Adventure-Works/Customers/">
      <CustomerID>1</CustomerID>
      <OutputParam>Hello World</OutputParam>
    </GetCustomerInfo>
  </soap:Body>
</soap:Envelope>

Specifying an invalid credential in the SOAP message results in the following SOAP fault being returned to the user:

<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xml:space="preserve" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-
ENV="https://schemas.xmlsoap.org/soap/envelope/" 
xmlns:sql="https://schemas.microsoft.com/sqlserver/2004/SOAP" 
xmlns:sqlsoaptypes="https://schemas.microsoft.com/sqlserver/2004/SOAP/types" 
xmlns:sqlrowcount="https://schemas.microsoft.com/sqlserver/2004/SOAP/types/SqlRowCount" 
xmlns:sqlmessage="https://schemas.microsoft.com/sqlserver/2004/SOAP/types/SqlMessage" 
xmlns:sqlresultstream="https://schemas.microsoft.com/sqlserver/2004/SOAP/types/SqlResultStream" 
xmlns:sqltransaction="https://schemas.microsoft.com/sqlserver/2004/SOAP/types/SqlTransaction" 
xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes">
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault xmlns:sqlsoapfaultcode="https://schemas.microsoft.com/sqlserver/2004/SOAP/SqlSoapFaultCode">
      <faultcode>SOAP-ENV:Client</faultcode>
      <faultstring>
         There was an error in the incoming SOAP request packet:  
         Client, LoginFailure, AccessDenied
      </faultstring>
      <faultactor>https://schemas.microsoft.com/sqlserver/2004/SOAP</faultactor>
      <detail xmlns:SOAP-1_2-ENV="http://www.w3.org/2003/05/soap-envelope">
        <SOAP-1_2-ENV:Code>
          <SOAP-1_2-ENV:Value>SOAP-1_2-ENV:Sender</SOAP-1_2-ENV:Value>
          <SOAP-1_2-ENV:Subcode>
            <SOAP-1_2-ENV:Value>sqlsoapfaultcode:LoginFailure</SOAP-1_2-ENV:Value>
            <SOAP-1_2-ENV:Subcode>
              <SOAP-1_2-ENV:Value>sqlsoapfaultcode:AccessDenied</SOAP-1_2-ENV:Value>
            </SOAP-1_2-ENV:Subcode>
          </SOAP-1_2-ENV:Subcode>
        </SOAP-1_2-ENV:Code>
        <SOAP-1_2-ENV:Reason>
          <SOAP-1_2-ENV:Text xml:lang="en-US">
            There was an error in the incoming SOAP request packet:  
            Sender, LoginFailure, AccessDenied
          </SOAP-1_2-ENV:Text>
        </SOAP-1_2-ENV:Reason>
        <SOAP-1_2-ENV:Node>https://srikr-800.redmond.corp.microsoft.com:443/sql/sql_auth</SOAP-1_2-ENV:Node>
        <SOAP-1_2-ENV:Role>
            https://schemas.microsoft.com/sqlserver/2004/SOAP
        </SOAP-1_2-ENV:Role>
        <SOAP-1_2-ENV:Detail />
      </detail>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This solution leverages only HTTP, SOAP, BASIC authentication and SSL, which make it ideal for heterogeneous environments. In the section below we see how one can leverage this in creating Perl scripts to connect directly to SQL Server.

Perl Scripts for Administration and Monitoring

With Web services in SQL Server 2005, one can connect to SQL Server from any platform that has Web service support. To illustrate this interoperability, we will create Perl scripts to connect to SQL Server. Perl is quite commonly used to create scripts to assist in administration and monitoring of the database server.

The example below illustrates how one can create a Perl script to monitor the state of the database. SQL Server 2005 has introduced support for dynamic management views that provide dynamic state information about the running server. In this example, we create a Perl script to monitor the number of active connections to the database by querying a dynamic view named dm_exec_connections.

It is assumed that the machine running this code already has Perl properly installed and configured.

The example here uses the ActiveState 5.8.x Perl package. The script takes advantage of the following packages:

We need SSL in this case because the Perl application is going to use both BASIC auth and SQL-Auth, and the XML packages are required to parse the response and display the results.

**Note   **For the complete script, see the associated download.

The following block of code instantiates the SOAP Lite package. We need to explicitly request for the output to be formatted as XML so that we can parse the response.

my $soap = SOAP::Lite
  -> uri('http://Adventure-Works/Customers/')
  -> proxy('https://srikr-800/sql/sql_auth')
  -> outputxml(1);

Next we need to set up the credentials for this connection. Since we intend to use SQL-Auth, we need to initialize the UsernameToken header as shown below.

# sample Yukon security SOAP header
# <wsse:Security  xmlns:wsse=\"http://docs.oasis-
open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
#   <wsse:UsernameToken>
#          <wsse:Username>sql_user</wsse:Username>
#       <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01
      /oasis-200401-wss-username-token-profile-1.0#PasswordText">
         foo-bar1
      </wsse:Password>
#   </wsse:UsernameToken>
# </wsse:Security>
my $Username = SOAP::Data->name('Username' => 'AdminUser');
my $Password = SOAP::Data->name('Password' => 'password')
            ->attr({Type => 'http://docs.oasis-open.org/wss/2004/01
            /oasis-200401-wss-username-token-profile-1.0#PasswordText'});
my $UsernameToken = SOAP::Data->name('UsernameToken')
            ->value(\SOAP::Data->value($Username, $Password));
my $security = SOAP::Header->name(Security)
            ->attr({'xmlns' => 'http://docs.oasis-open.org/wss
            /2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'})
            ->value(\$UsernameToken);

The BASIC authentication credentials are passed in by implementing the following stub:

sub SOAP::Transport::HTTP::Client::get_basic_credentials {
  return 'User' => 'Password';
}

**Note   **It is not a recommended practice to store/reference passwords in the script files. The user should follow standard security guidelines when dealing with passwords.

Next we invoke the Web method. Since we are going to execute a T-SQL batch statement, the code looks like this:

# Invoking a sqlbatch to retrieve the number of connections 

$soap -> on_action (sub { return '""';});

$method = SOAP::Data->name('sqlbatch')->attr({xmlns => 
'https://schemas.microsoft.com/sqlserver/2004/SOAP'});
@param = ( SOAP::Data->name(BatchCommands => 'select session_id, 
net_transport, protocol_type from sys.dm_exec_connections'));

Finally we parse the XML response to retrieve the data:

for my $node($doc->getElementsByTagName("row"))
{
  
  print "\n";
  for my $kid ($node->getChildNodes)
  {
    print $kid->getNodeName(); print ":: ";
    for my $gkid ($kid->getChildNodes)
      {
         print $gkid->getNodeValue();  #print the actual values for the columns
      }
    
    print "\t";
  }
 
  print "\n";
}

Running this Perl script generates the following output:

Testing SOAP::Lite client against AdventureWorks Contacts sample web service.
Calling sqlbatch
Server response...

Server response...
session_id:: 54 net_transport:: HTTP    protocol_type:: SOAP    
connection_id::5EC2B4E2-39A6-4FA7-BBDB-144DAED59A41

session_id:: 53 net_transport:: Shared memory   protocol_type:: TSQL    
connection_id:: 5AE50B7D-D919-4FBC-BA42-6069A12F4D30

session_id:: 53 net_transport:: Session protocol_type:: TSQL    
connection_id::05830BE9-F12F-429D-BBAC-E4EEB2C528EF    
parent_connection_id:: 5AE50B7D-D919-4FBC-BA42-6069A12F4D30

The above output indicates that there are two connections to SQL Server: one using the binary protocol, TDS, shows up as protocol_type:: TSQL, and the other connection corresponds to the SOAP/HTTP connection made when running this Perl script.

I would like to draw your attention to the session_id column. This session id matches the spid (Session Process Identifier) in the database engine that is associated with this request. There are two entries with spid equals 53 because one corresponds to the physical connection (net_transport is shared memory), and the other corresponds to the logical session that is layered over the same physical connection. (For more information on multiple active result sets, see Multiple Active Result Sets (MARS) in SQL Server 2005.) This is evidenced by the fact that the parent_connection_id for the logical session matches the physical connection. In the case of TDS, connections and sessions are tied together; in other words, one cannot join an existing session from a different physical connection. SOAP access enables a user to join an existing session by specifying the appropriate sessions header in the request. Using multiple sessions in SOAP is a topic for a different article. Interested readers can read SQL Server 2005 Books Online for more information on how to enable and use sessions.

Additional Scenarios

Let us now look at a few additional scenarios. Most database applications have a lot of data-centric logic built into stored procedures. Native XML Web services take advantage of this investment in stored procedures by making it easy to expose them as Web services. Additionally, native Web services can improve performance because the data access is happening in-proc instead of being shipped to the mid-tier process.

Lookup Services

When SQL Server is hosting data for reference/lookup, a Web service is an ideal mechanism to expose this data. In this scenario, the database acts as a repository for potentially a large amount of data. The Web services leverage the database engines query processing capabilities to obtain the results. The result set in such queries is well defined and in the order of a few kilobytes. Examples of such scenarios include:

  • product catalogs
  • location-aware Web services that return area-specific information (weather, traffic) to your users
  • employee directories for intranets

Report Generation Services

There are many scenarios where the database server hosts data that serves as the basis for reporting. Within an intranet, it is convenient to expose these reports as Web services. One can easily create a T-SQL stored procedure to generate a report and expose it using Web services in SQL Server 2005. You can also easily embed the results of Web services into Office applications such as Excel and InfoPath. Not only does it make it easier for client applications to retrieve data, it also frees the database administrator from the burden of supporting additional infrastructure in order to expose Web services. Users also have the ability to run ad hoc queries and generate reports using the batch access capabilities of native Web services.

Cross Platform Access to User Defined Types

SQL Server 2005 introduces support for user-defined types. With user-defined types (UDTs), you can extend the scalar type system of the database (beyond just defining your own alias for a system type, which has been available in previous releases of SQL Server). One could, for example, define a UDT type called Point that captures the x and y coordinates of a point. Native Web services leverage the serialization framework available in the common language runtime and enable transport of such types as XML. The client platform can then deserialize this XML into objects defined on their platform. This enables a Java client to send and receive UDT instances.

Mobile Scenarios

Any device that can parse XML and submit HTTP requests can now access SQL Server. This, combined with the ability to rejoin an existing session when the connection is dropped, makes it ideal to develop applications for mobile or sporadically connected devices, which in turn, enables anytime, anywhere access to SQL Server.

Asynchronous Services

Native Web services combined with SQL Service Broker (also available with SQL Server 2005) can be used to construct a solution that provides asynchronous services. Consider an order processing workflow. One can expose a SQL Server 2005 Web service that receives an order and responds with immediate acknowledgment that it has received the order. We could then enter the order in a service broker queue for processing. Order fulfillment might require invoking other Web services. When the order is fulfilled we could notify the client using any notification mechanism the client has subscribed to.

Conclusion

Native XML Web services leverage your investment in the database server and enable your database to participate as a service provider. I've shown in detail how you can use this feature to provide access to data hosted in SQL Server in heterogeneous environments and described additional scenarios that are appropriate for native Web services. Native XML Web services promote interoperability and facilitate outreach by enabling a broader range of clients to connect to SQL Server.