This chapter begins by covering a few more important aspects of Microsoft Transaction Server (MTS). It explains how MTS extends COM security with its own role-based security model, and it shows you how to share global data among MTS objects using the shared property manager. The chapter also explains how to create Web-based applications that leverage the COM and MTS techniques that you've learned in this book. I describe the IIS/ASP architecture and show you how to create Visual Basic objects from an Active Server Pages (ASP) page. You can create ASP applications that allow Web
The chapter concludes with a description of important design issues that you should consider when you build a distributed application. As you know, MTS provides a transaction processing monitor and a few other critical middle-tier services. However, you must provide many other essential pieces of middleware yourself. This chapter explains how to create a scalable
Chapter 8 described the security models of Microsoft Windows NT and COM as they existed before the introduction of MTS. As you'll recall, each Windows NT user account and
Identity is also an important concept in COM security. An administrator should configure the
AppID
for each distributed application using a
RunAs
setting so that the system
Chapter 8 also explained how COM offers both declarative security and programmatic security. Declarative security is beneficial because it transfers the responsibility for securing a distributed application from programmers to administrators. Programmatic security provides more control and flexibility than declarative security. However, there's little you can do on the programmatic side of COM security using Visual Basic. Unless you're prepared to write some supporting code in a C++ DLL, you must run your Visual Basic applications using declarative security settings stored in the local Registry.
As you know, COM security is built on top of Windows NT security and the Remote Procedure Call (RPC) layer. COM can leverage the accounts database and SID management as well as authentication and authorization checking from the underlying system. It's important to understand how all this works because MTS security is layered on top of COM security. MTS uses COM authentication to validate users. MTS also uses COM's activation security to control which Windows NT user account serves as the identity for a server package.
However, MTS doesn't use the same authorization scheme as COM. One shortcoming of the access control provided by COM is that it lacks sufficient granularity. You can configure only two types of permissions for an application: access permissions and launch permissions. This means that access control is an all-or-nothing proposition. You can either grant or deny access permissions on an application-wide basis for each Windows NT user or group. Once you let a user in the door, you can't control what the user can do. MTS
In Windows NT 4, every COM application should be configured with its own
AppID
. An MTS application is no exception. (An MTS server package is an MTS application.) MTS creates and
The designers of MTS decided to replace COM access control with their own version. The AppID behind an MTS server package is configured to use the default machinewide launch permissions; however, when the package is launched, MTS grants access permissions to all users. In effect, MTS turns off COM's access control. All users must be able to get past the security checkpoints of COM in order to reach those of MTS.
MTS extends COM security with its own security model using the concept of a role . A given user can be assigned to multiple roles. A role is a set of users within an MTS application that have the same security profile. When you design a secured MTS application, you should define an appropriately named role for each type of user. For example, you can design an application with three roles: Reader, Writer , and Manager. In MTS, both declarative and programmatic security checks are performed on the roles you define instead of on SIDs, as in COM. The MTS security model thus offers more control and flexibility than does COM.
Let's look at declarative security first. When you create a new server package, declarative authorization checking is turned off by default. You can
One of the biggest benefits of roles is that you can preconfigure the permissions for the entire application in the development environment. Permissions in MTS have no dependencies on the SIDs from the Windows NT accounts database. This makes the role-based model much more flexible than the COM security model, in which access permissions are assigned directly to SIDs. The concept of roles is more natural for developers, and the use of roles eases deployment of a single package in multiple production environments.
Another benefit of this model is that you can configure access permissions on a component-by-component as well as an interface-by-interface basis. This declarative security scheme provides much more control than COM security. You can also configure declarative security at the interface level, which provides yet another motivation to base the design of your Visual Basic applications on user-defined interfaces.
For example, you can create a CCustomers component that implements two user-defined interfaces, IQueryCustomer and IUpdateCustomer . When you set up the permissions for your application, you can configure every role in the package to have access to the IQueryCustomer interface but restrict access to the IUpdateCustomer interface for every role except Writer. Stop and think about the implications of this example. I simply can't overemphasize how powerful declarative security checks are at the interface level.
Yes, I know that user-defined interfaces bring along some additional complexity, which makes many Visual Basic programmers run and hide. It's easy to fall back on Visual Basic's ability to create and implicitly cast to the default interface behind the scenes. And
You always have the ability to define user-defined interfaces and implement them in your Visual Basic components. As you remember, Chapter 6 covered what your options are in terms of using Interface Definition Language (IDL) as well as using the Visual Basic IDE. The key point is that when you design your
A role is defined within the scope of an MTS server package. That is, each role exists within a single MTS application (running process). When a server package is launched, the MTS container application (Mtx.exe) and the MTS executive (Mtxex.dll) work together to set up the role-based authorization checking scheme.
Package security isn't available with library packages. Library packages aren't secured because they are loaded into the address space of an unknown client application. There's no guarantee that the client application will be a secured MTS application. Furthermore, you must set up role-based authorization checking when the hosting process is launched. You can't do this with library packages because of their passive nature.
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}What this boils down to is that roles and role membership have no meaning within an MTS library package. When you attempt to create a role or assign role membership in a library package, the MTS Explorer displays a dialog box indicating that package security isn't available. You must be careful because a client application can defeat the MTS security model by creating objects directly from a library package. When a non-MTS client creates objects from a library package, the application is unsecured and the role-based security features of MTS won't function properly.
As you've seen, each class and interface can have one or more associated roles in its membership. If authorization checking is enabled, the caller must be in at least one role in order to successfully invoke a method, as shown in Figure 12-1. If MTS discovers that the caller doesn't belong to any role associated with the component or the interface in use, it fails the call and returns the well-known HRESULT
E_ACCESSDENIED
. If the caller is a Visual Basic application, the Visual Basic run time catches the HRESULT
E_ACCESSDENIED
and raises a trappable "Permission
Figure 12-1.
MTS
MTS performs authorization checks only on calls from outside the package, not on intrapackage (intraprocess) calls. Intraprocess calls are therefore much faster. However, because intraprocess calls are assumed to be secure, you must protect your applications from
Let's say that you have component A and component B inside a server package in which you have enabled authorization checking. You've placed Role1 in the membership of component A and Role2 in the membership of component B, as shown in Figure 12-1. Any user who is a member of Role1 can directly invoke a method on objects of type A but not on objects of type B. But what happens if a Role1 user invokes a method on an object of type A, which in turn invokes a method on an object of type B? The call will succeed because there are no intraprocess security checks. You should understand the implications of this example when you design components that make intraprocess calls.
Calls between server packages can also be tricky. For example, what happens if client A makes a call on server package B, which then makes a call on server package C? You might expect server package C to perform its security checks using client A's security credentials, but this isn't the case. MTS performs its security checks from hop to hop as opposed to performing them from end to end. This means you must understand the difference between the direct caller and the original caller . The direct caller is the party one hop up the call chain, while the original caller is the party at the top of the call chain. MTS always performs its security checks using the credentials of the direct caller.
In this example, client A is the original caller and server package B is the direct caller. Server package C conducts its security checks using the Windows NT user account that serves as the identity for server package B. As you can see, you must be careful. When you grant a role access permissions to a component, you must be certain that the component itself doesn't make any intraprocess calls or interprocess calls that will permit access to other components that should be off-limits to the role in question.
As you've seen, the declarative security available in MTS (in its current release) offers access control at both the component and the interface level. COM+ will likely extend this control down to the method level. However, if you need method-level granularity now, you must resort to programmatic security. Fortunately, programmatic security is much easier in MTS than it is in COM. MTS exposes a handful of properties and methods through the ObjectContext interface, making programmatic security easily accessible to Visual Basic programmers.
MTS offers programmatic security to complement and extend what is possible through declarative security. You can write your own security checks using the ObjectContext interface. For example, you can test a call to see whether it's running inside a secured MTS application before you attempt a secured operation:
Const E_ACCESSDENIED = &H80070005
Dim ObjCtx As ObjectContext
Set ObjCtx = GetObjectContext()
If ObjCtx.IsSecurityEnabled() Then
' Conduct secured operation.
Else
' Raise an access-denied error to caller.
Err.Raise E_ACCESSDENIED, _
"MyDll.MyClass.MyMethod", _
"Permission denied"
End If
IsSecurityEnabled
You can also write code to test whether the caller belongs to a particular role. The IsCallerInRole method accepts a single string parameter of the role you want to test. This method returns True if either the Windows NT user account or one of the Windows NT group accounts of the caller maps to this role. IsCallerInRole always returns True when it's called inside an unsecured application. You should therefore call IsCallerInRole together with IsSecurityEnabled , like this:
Dim ObjCtx As ObjectContext
Set ObjCtx = GetObjectContext()
If ObjCtx.IsSecurityEnabled() And _
ObjCtx.IsCallerInRole("Manager") Then
' Conduct a secured, manager-only operation.
Else
Err.Raise E_ACCESSDENIED, _
"MyDll.MyClass.MyMethod", _
"Permission denied"
End If
As you can see, IsCallerInRole lets you perform access checking at the method level. It also lets you perform conditional access checks. For example, you can write the SubmitOrder method so that only users in the role of Manager can submit a purchase order if the request exceeds $5,000:
Const E_ACCESSDENIED = &H80070005
Dim ObjCtx As ObjectContext
Set ObjCtx = GetObjectContext()
Dim Amount As Currency
' Determine the purchase amount.
Amount = GetAmount()
' Check for manager role on any amount that exceeds ,000.
If Amount > 5000 Then
If Not ObjCtx.IsSecurityEnabled() Or _
Not ObjCtx.IsCallerInRole("Manager") Then
' The caller isn't a manager, and the amount is more than ,000.
ObjCtx.SetAbort
Dim ErrDesc As String
ErrDesc = "Manager required when amount exceeds ,000"
Err.Raise E_ACCESSDENIED, , ErrDesc
End If
End If
' Now conduct secured operation.
You can call
IsCallerInRole
from a component inside a library package, but such a call can check only against the roles defined inside the server package in which the library package has been loaded. MTS also generates an
mtsErrCtxRoleNotFound
error if you call
IsCallerInRole
inside a secured MTS application and pass a role
In addition to the role-based programmatic security you've just seen, MTS can provide the
Dim ObjCtx As ObjectContext
Set ObjCtx = GetObjectContext()
Dim Caller As String
Caller = ObjCtx.Security.GetOriginalCaller()
This example shows how to use the Security property of the ObjectContext interface. The Security property lets you call the GetOriginalCallerName method. This method returns the caller's account name in an Authority\User format, as in MyDomain\TedP . In addition to GetOriginalCallerName , the Security object also exposes GetDirectCallerName , GetDirectCreatorName , and GetOriginalCreatorName . The concepts of original creator and direct creator work in exactly the same way as those of original caller and direct caller explained earlier in this chapter.