The Render Web Service

The RenderService class provides the functionality that allows the client application to query task information and submit new tasks. To call any RenderService Web method, the client must first log on and receive a valid ticket.

RenderService Authentication

The ticket information is encapsulated in a custom SoapHeader class called TicketHeader. Although this class defines four pieces of information, not all of this information will be present at the same time. That's because username and password information is erased immediately after login, and at the same time the corresponding user ID and ticket GUID are inserted. (See Listing 18-16.)

Listing 18-16 The SOAP authentication header
 Public Class TicketHeader     Inherits SoapHeader     Public UserName As String     Public Password As String     Public UserID As Integer     Public SessionGUID As Guid End Class 

The TicketHeader class is defined in the XML Web service .asmx file, but it is also added to the proxy file. The first task the client needs to accomplish is to create a TicketHeader object, set the username and password information, and apply the header to the proxy class. Then the client can call the Login Web method, which examines and modifies the header as required.

Here's an example of the client code, which retrieves the user login information from a login window:

 Dim Proxy As New localhost.RenderService() ' Create the SOAP header with the user name and password. Dim Ticket As New localhost.TicketHeader() Ticket.UserName = frmLogin.UserName Ticket.Password = frmLogin.Password ' Assign the header to the proxy instance. Proxy.TicketHeaderValue = Ticket ' Try to log in. If Proxy.Login() Then     ' The login attempt was successful. Else     ' The login attempt failed. End If 

Listing 18-17 shows the Login Web method, which verifies the account information and assigns the ticket. Note that the Login method includes a SoapHeader attribute. This attribute serves three purposes. First, it indicates that this method requires the authentication header. Without this piece of information, the header will not be sent with the client's request message. Second, it indicates which member variable will be used to receive the header. In this case, the variable is named Ticket. Third, the SoapHeader attribute adds the optional Direction parameter and specifies that the SOAP header should be sent back to the client at the end of the method. Otherwise, the changes to the two ticket values (Ticket.SessionGUID and Ticket.UserID) wouldn't take effect.

Listing 18-17 The RenderService.Login method
  ' Check user name and password from SOAP header. ' If they can be authenticated, add a session record, ' remove the user password from the header (for security), ' and add the ticket GUID. ' Returns False if the client could not be successfully logged in. <WebMethod(), _  SoapHeader("Ticket", Direction:=SoapHeaderDirection.InOut)> _ Public Function Login() As Boolean     Dim UserID As Integer     UserID = Tables.Users.AuthenticateUser(Ticket.UserName, _                                            Ticket.Password)     If UserID <> 0 Then         ' Clear the password for enhanced security.         Ticket.Password = ""         Ticket.UserName = ""         Ticket.UserID = UserID         ' Create the session and assign the GUID to the SOAP header.         Dim GUID As Guid = Tables.Sessions.CreateSession(UserID)         Ticket.SessionGUID = GUID         Return True     Else         Return False     End If End Function 

After the login has been completed successfully, the client does not need to take any further action. The ticket remains "attached" to the current proxy class instance and is automatically sent with each Web method request that requires the header. Figure 18-4 shows how a typical session record appears in the database.

Figure 18-4. A session record

graphics/f18dp04.jpg

Every other Web method in the RenderService class also includes an attribute indicating that it requires the TicketHeader. The first line in the method then calls a private AuthenticateSession function, which examines the supplied session GUID and user ID information. This pattern is shown in Listing 18-18.

Listing 18-18 Authenticating a ticket
 <WebMethod(), SoapHeader("Ticket")> _ Public Sub DoSomething()     AuthenticateSession(Ticket)     ' (Actual method code goes here.) End Function ' Throws an exception if the session is not valid. ' Currently, sessions are allowed to exist indefinitely, but you ' could use this method to examine the CreateDate value and impose ' an expiration date. Private Sub AuthenticateSession(ByVal ticket As TicketHeader)     If Tables.Sessions.GetSession(ticket.SessionGUID, ticket.UserID) _       Is Nothing Then         ' No record was returned. Raise an exception.         Throw New Security.SecurityException("Session not valid.")     Else         ' The session exists. Allow the code to continue.         ' (This is where you could add an expiration policy.)     End If End Sub 

Optionally, you can use the AuthenticateSession function to check the creation date and remove an expired ticket. Because this expiration policy is implemented in code, you have the freedom to tailor it to your needs, possibly creating expiration rules that are user-specific or that depend on the time of day.

Note

One reason a session ticket is used is that it can protect user account information. Typically, a system uses an SSL connection to ensure that requests to the Login method are encrypted. After that point, requests can use fast, clear-text transmission because the login information is no longer used. At worst, a session GUID could be intercepted and a session could be hijacked until it expires. For more information, refer to the ticket-based security discussion in Chapter 13.


It is possible to use a simpler login approach that doesn't require the client to create a TicketHeader object. To do this, you create a Login method that accepts username and password information through two parameters:

 <WebMethod(), _  SoapHeader("Ticket", Direction:=SoapHeaderDirection.Out)> _ Public Function Login(userName As String, password As String) _   As Boolean     ' (Authentication code omitted.) End Function 

This approach is sometimes preferred because it is more transparent for the client. However, hiding more details can also confuse the client programmer, who might not be aware of the behind-the-scenes process and what causes a session to time out.

Finally, when the client is finished, it's good practice to call the Logout method shown in Listing 18-19 to ensure proper cleanup of the session record.

Listing 18-19 The RenderService.Logout method
 <WebMethod(), SoapHeader("Ticket")> _ Public Sub Logout()     Tables.Sessions.RemoveSession(Ticket.SessionGUID) End Sub 

RenderService Functionality

The RenderService class provides two more Web methods: GetTasks and SubmitTask. The GetTasks method (shown in Listing 18-20) is the simplest it just examines the user ID information in the SOAP authentication header, invokes the database component, and returns a DataSet with the corresponding information. The client doesn't need to submit any information in method parameters.

Listing 18-20 The RenderService.GetTasks method
  ' Returns all the tasks for the currently logged in user. <WebMethod(), SoapHeader("Ticket")> _ Public Function GetTasks(ByVal completedOnly As Boolean) As DataSet '     AuthenticateSession(Ticket)     Return Tables.Tasks.GetTasks(Ticket.UserID, completedOnly) End Function 

The SubmitTask method (shown in Listing 18-21) performs a little more work. It adds a record to the database for the new task and copies the file onto the server. The unique-task GUID is used for the filename, ensuring that there can't be a naming conflict (and that malicious users can't guess which project a rendered file corresponds to).

However, there is one wrinkle with this approach: To use the GUID as the filename, the task record must be added before the file is transferred. Usually, these two tasks are performed in the opposite order, ensuring that if there is a file-transfer error (which could be caused by network problems), the record isn't added. To overcome this problem, the entire Web method executes inside a COM+ transaction. This means that if an exception is generated while the file is being written, the database insert operation is rolled back.

Listing 18-21 The RenderService.SubmitTask method
  ' Transfers the source file as byte array. Returns the GUID ' that identifies the task. ' This method adds a task record, and ' saves a file with the date and GUID as a filename. <WebMethod(TransactionOption:=TransactionOption.RequiresNew), _  SoapHeader("Ticket")> _ Public Function SubmitTask(ByVal buffer As Byte(), _   ByVal fileName As String) As Guid     AuthenticateSession(Ticket) 
     ' Create a corresponding task record for the transferred file.     Dim Task As New DBComponent.TaskDetails()     Task.SourceName = fileName     Task.Status = DBComponent.TaskStatus.NewAdded     Task.UserID = Ticket.UserID     ' You must add the record before the file is transferred,     ' in order to get the GUID, which is used for the file name.     ' To ensure that the record is not committed in the case of an     ' error, this Web method executes inside a COM+ transaction.     Dim TaskGuid As Guid = Tables.Tasks.AddTask(Task)     ' Prepare to save the file.     Dim SavePath As String     SavePath = ConfigurationSettings.AppSettings("SourceDirectory")     ' Make sure the directory does not end with a backslash.     SavePath.Trim("\")     SavePath &= "\" & TaskGuid.ToString()     Dim fs As New FileStream(SavePath, FileMode.CreateNew)     fs.Write(buffer, 0, buffer.Length)     fs.Close()     Return TaskGuid End Function 

Note

The code in Listing 18-21 contains one quirk. The transaction is started in the XML Web service, but the database component also participates. If the transaction fails, the database operations are rolled back. This is exactly what you want, but it might strike you as a little odd because the database component does not use COM+. So how can it participate in a COM+ transaction?

The answer is as follows: when the XML Web service transaction is created, a new unmanaged context is created and associated with the current thread. The database component is not associated with any specific unmanaged context, so the common language runtime (CLR) automatically provides the database component with the current transactional context. In other words, .NET automatically enlists the database object in the transaction.

This approach is extremely convenient but subject to one caveat: It's not guaranteed to work in future versions of the .NET Framework. If .NET is rewritten entirely in managed .NET code at some point, this behavior will most likely change and you will have to specifically configure transactional components with attributes.




Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

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