8.2 Authentication


The non-HTTP classes share a trait that complicates the authentication layer of the WishListCustomer application. Each server class hands its requests to the SOAP::Server class directly, without processing headers like the HTTP classes do. Some classes represent cases where there is no native concept of headers (such as the TCP class); others, like the POP3 class, do have a concept of headers. However, none of these classes create HTTP::Request objects, so direct access to headers isn't inherently possible. As such, cookies can't be relied on to provide the authentication.

8.2.1 Server Authentication

Without cookies, the server classes must rely on the presence of authentication data in the header of the SOAP envelope. There are two ways to do this:

  • Create a new version of the WishListCustomer::SOAP layer that inherits from SOAP::Server::Parameters in order to have access to the request envelope. Using the envelope, read the headers for the desired information.

  • Subclass the right components on the server-class side so that at some point in the lifecycle of processing the request, the headers may be retrieved and the relevant authentication data converted to the hash table format the WishListCustomer::SOAP layer already uses.

Taking the second approach means leaving the SOAP layer untouched, which is the preferred option. This lowers the chance of a change affecting the HTTP applications and also permits their clients to continue to function without any changes.

8.2.1.1 Designing the new scheme

Because the WishListCustomer class and its SOAP layer are fairly specific about the form and function of the cookie-authentication scheme, the header implementation of the scheme is obliged to conform to this.

To do this, the scheme defines a single header element called authenticate . This element may include an attribute called name (default value: user ) whose value is the name of the cookie that the HTTP code expects. The value of the element acts as the value of the cookie and should be in the format of an HTTP cookie, with all character-encoding intact.

The header doesn't set the mustUnderstand attribute, because servers, such as the HTTP servers, that happen to get such a header shouldn't be prevented from functioning. Instead, let the class methods create their own faults for the absence of authentication data.

A typical header looks something like this (with some formatting of the XML done for readability):

 <wlc:authenticate xmlns:wlc="urn:/WishListCustomer"                   name="user"> %2572%256a%2572%2561%2579%253a%253a%2532%2532%2537%2536%2534 %2564%2561%2535%2535%2535%2533%2563%2532%2530%2563%2563%2538 %2530%2563%2563%2535%2537%2539%2564%2562%2535%2562%2564%2532 %2532%2535%2537 </wlc:authenticate> 

The server class is responsible for ensuring that any extra whitespace or newlines are removed.

8.2.1.2 Creating a generic server class

To create the server class once and once only, we will describe a generic server class that can be used for any transport protocol described in this chapter. The reason for this is simple: all the non-HTTP protocols need the functionality, and all use identical interfaces for server configuration. Their differences are all at the request-handling level, something that's addressed in the application anyway.

The new server class has to inherit from the basic type the server application is being built on. However, the method that is going to be overloaded to accomplish the task comes from SOAP::Server , and as such is the same for all the non-HTTP classes.

There is no reason to define a completely new class for each server protocol that follows . Instead, the class uses the special import method to allow the super-class to be specified at will. The new class has to provide a definition of import to manage the class hierarchy, and a definition of find_target that looks at and acts upon any relevant header blocks. Example 8-1 shows the class.

Example 8-1. The WishListCustomer::Transport class
 package WishListCustomer::Transport;     use strict; use vars qw(@ISA); use subs qw(import find_target);     use SOAP::Lite;     # For lack of a better default: @ISA = qw(SOAP::Server);     1;     sub import {     my $class = shift;     my $new_parent = shift;         @ISA = ($new_parent); }     # This remains coupled to WishListCustomer::SOAP by virtue # of the use of the %WishListCustomer::SOAP hash table. sub find_target {     my $self = shift;     my $request = shift;         %WishListCustomer::SOAP::COOKIES = ( );     my $header = $request->match(SOAP::SOM::header .                                  '/authenticate')->dataof;     if ($header) {         my $key = $header->attr->{name}  'user';         my $value = $header->value;             $value =~ s/\n\r\s//g;         $WishListCustomer::SOAP::COOKIES{$key} = $value;     }     $self->SUPER::find_target($request); } 

Applications using the generic class are still responsible for loading the specific server-class code. This is easier and safer than expecting the generic class to derive from its input the proper name for a require statement. Even though the server classes in the SOAP::Lite distribution can be transformed by dropping the last element from the class name (for example, SOAP::Transport::TCP::Server yields SOAP::Transport::TCP ), that convention can't be relied on.

8.2.2 Client Authentication

The client classes have their role to play in accommodating the envelope-based authentication as well. Unlike server applications, however, clients don't explicitly load the transport-level code, so there is no real need for a subclass that compares to WishListCustomer::Transport . What the clients do need is a convenient way to create and send headers for the credentials that requests need. Fortunately, the SOAP::Lite package provides easy support for creating header elements and passing them around.

8.2.2.1 Custom headers with SOAP::Header

The SOAP::Lite package provides a class called SOAP::Header that is an empty subclass of SOAP::Data . Recall that the SOAP::Data class encapsulates simple Perl data and adds SOAP elements such as name, type, and namespace. The purpose of SOAP::Header is to allow the serialization layer to distinguish header elements from straightforward data. By instantiating objects using this class, the serialization takes care of the rest.

Based on the design of the authentication headers presented earlier, the header has the name authenticate and has the fully encoded cookie as the content of the tag. There is no need to set the name attribute, because the default value of user is correct for any of the applications. Because the value for mustUnderstand also matches the default, it isn't specified, either.

The example clients in the following sections directly use the make_cookie method from the WishListCustomer class to create the authentication token. The basic code to create a header object is:

 $cookie = WishListCustomer::make_cookie($user, $password); $header = SOAP::Header->name(authenticate => $cookie)                       ->uri('urn:/WiahListCustomer'); 

With these lines of code, the variable $header can be used as often as needed to provide the authentication in SOAP messages. Because the http clients use cookies (which are sent with every request), this allows for a similar design approach by allowing a header that is computed once and then reused between requests.

8.2.2.2 WishListCustomer::Client as a shortcut

The client applications don't need to use a generic class to simplify choosing the transport protocol. However, something that is very useful is a mechanism that automatically manages the authentication layer itself.

The key to the server-oriented class is finding the best point to insert a localized method that has access to the request object after serialization, but before dispatch. For the client, the trick is to find a point at which the call has been broken down into method name and arguments, but has not yet been serialized. Because the SOAP::Lite package does some creative work with AUTOLOAD to allow dispatch of remote methods to appear like local class methods, the subclass should continue to allow that to take place. In the case of SOAP::Lite , the place to insert the subclass is the call method.

The call method in SOAP::Lite starts the conversion of a method name and a list of data arguments into a remote invocation and response. It is called with the method name (a string or a SOAP::Data object) followed by zero or more parameters. All the subclass has to do is privately maintain a header object with the authentication data and then insert it into every invocation of call.

The management of the credentials is a simple method call that takes the username and password as arguments, with optional additional attribute support thrown in just to be safe. The full WishListCustomer::Client class is provided in Appendix D, but the overloaded call method is shown in Example 8-2.

Example 8-2. The WishListCustomer::Client::call method
 sub call {     my ($self, $method, @args) = @_;         unshift(@args, $self->{_ _auth_header})         if $self->{_ _auth_header};     $self->SUPER::call($method, @args); } 

When no credentials have been set, nothing is added to the outgoing call.



Programming Web Services with Perl
Programming Web Services with Perl
ISBN: 0596002068
EAN: 2147483647
Year: 2000
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net