[ Team LiB ] Previous Section Next Section

19.2 Programming COM+ Security

In this section, we explain how to configure COM+ security with .NET attributes; we do this by creating and deploying an example serviced component. We demonstrate how to use COM+ RBS and PAS to control access to a simple component that tracks software defects for an imaginary software product called SecurityPro. In Section 19.3 , we demonstrate an alternative approach to configuring COM+ security.

We begin by defining the IDefectTracker interface, which contains the three methods we use to simulate defect tracking. We briefly describe the purpose of each interface method in the list below. We are developing this component to demonstrate COM+ security, so we do not define types to represent defects, or specify method arguments:

ViewAllDefects

Returns all of the pending defects for the product

CreateNewDefect

Creates a new record representing a pending defect

CloseDefect

Closes a defect and removes it from the pending list

The following statements define our simple IDefectTracker interface:

# C#

using System;

public interface IDefectTracker {

    void ViewAllDefects(  );

    void CreateNewDefect(  );

    void CloseDefect(  );
}

# Visual Basic .NET

Imports System

Public Interface IDefectTracker

    Sub ViewAllDefects(  )

    Sub CreateNewDefect(  )

    Sub CloseDefect(  )

End Interface

In the following sections, we create a serviced component that implements the IDefectTracker interface, and apply .NET attributes to configure COM+ RBS and PAS.

19.2.1 Creating the Serviced Component

A serviced component must inherit from the System.EnterpriseServices.ServicedComponent class and must be a member of a strong-named assembly. The following statements list the SecurityProTracker class, which extends ServicedComponent and implements the IDefectTracker interface.

# C#

using System;
using System.EnterpriseServices;

// Specify the file containing the key for assembly signing
[assembly: System.Reflection.AssemblyKeyFile("mykey.key")]

public class SecurityProTracker: ServicedComponent, IDefectTracker {

    public void ViewAllDefects(  ) {}

    public void CreateNewDefect(  ) {}

    public void CloseDefect(  ) {}
}

# Visual Basic .NET

Imports System
Imports System.EnterpriseServices

' Specify the file containing the key for assembly signing
<Assembly: System.Reflection.AssemblyKeyFile("mykey.key")>

Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

    Public Sub ViewAllDefects(  ) Implements IDefectTracker.ViewAllDefects
    End Sub

    Public Sub CreateNewDefect(  ) Implements IDefectTracker.CreateNewDefect
    End Sub

    Public Sub CloseDefect(  ) Implements IDefectTracker.CloseDefect
    End Sub

End Class

We do not discuss the members of the ServicedComponent class, which is outside the scope of this book; it is sufficient to understand that classes must extend ServicedComponent in order to be serviced components. Consult the .NET Framework for full details.

The emphasized statements specify the file containing the cryptographic key we use to create the strong name for our assembly; we create this file in Section 19.2.4. See Chapter 2 for details of assembly strong names. We have now satisfied the two requirements for COM+ serviced components, and we can compile and install our serviced component without any further changes.

19.2.2 Specifying the COM+ Application Type

The System.EnterpriseServices.ApplicationActivation class is the first COM+ attribute we apply, allowing us to specify whether we want a COM+ server or library application. This attribute accepts a single parameter, which must be a value from the System.EnterpriseServices.ActivationOption enumeration, the values of which are detailed in Table 19-1. Application proxies are created with the Component Services tool; consult the Windows documentation for details.

Table 19-1. The enumeration values

Value

Description

Library

Specifies that serviced components are activated in the client application process

Server

Specifies that serviced components are activated in a process provided by COM+

The emphasized statements in the code fragment below demonstrate the use of the ApplicationActivation attribute to specify a COM+ server application for our example component:

# C#

using System;
using System.EnterpriseServices;

// Specify the file containing the key for assembly signing
[assembly: System.Reflection.AssemblyKeyFile("mykey.key")]
// Specify that we want a COM+ Server application
[assembly: ApplicationActivation(ActivationOption.Server)]

public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

Imports System
Imports System.EnterpriseServices

' Specify the file containing the key for assembly signing
<Assembly: System.Reflection.AssemblyKeyFile("mykey.key")> 
' Specify that we want a COM+ Server application
<Assembly: ApplicationActivation(ActivationOption.Server)> 

Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

19.2.3 Applying the Security Attributes

In this section, we demonstrate how to use COM+ RBS and PAS, both of which are configured using .NET attributes from the System.EnterpriseServices namespace. COM+ security can be configured programmatically, as demonstrated in this part of the chapter, or administratively, as discussed in Section 19.3 . When applying .NET attributes to configure RBS and PAS, you must bear in mind that the system administrator can change the security configuration for your components or even disable security completely.

19.2.3.1 The ApplicationAccessControl attribute

The first security attribute to apply is ApplicationAccessControl, which acts as a master switch for COM+ application security and allows you to specify your PAS authentication and impersonation levels. This attribute is always applied to an assembly, because it affects the entire COM+ application. The ApplicationAccessControl attribute defines four properties:

Value

This is the master switch for COM+ security and is applied at the application level. As we explained at the start of this chapter, a COM+ application is made up of one or more serviced components, and this value enables or disables RBS and PAS security for every component in the application. If this property is set to false, then no validation of client identity will be performed and no access control will be enforced.

AccessChecksLevel

This property accepts a value from the AccessChecksLevelOption enumeration, which defines two values, detailed below. If the Value property is set to false, then this property has no effect, since COM+ security is disabled.

Application

The Application value enables PAS only; client identity will be authenticated according to the value of the ApplicationAccessControl attribute's Authentication property (see below), but COM+ RBS is not enforced. This is the default used if no value is specified in the attribute declaration.

ApplicationComponent

The ApplicationComponent value specifies that PAS and RBS will be enabled.

Authentication

The Authentication property accepts a value from the AuthenticationOption enumeration, whose seven values refer to the authentication levels we discussed in Section 19.1.2.

ImpersonationLevel

The ImpersonationLevel property accepts a value from the ImpersonationLevelOption enumeration, whose values refer to the impersonation levels we discussed in Section 19.1.2.

The emphasized statements below show the use of the ApplicationAccessControl attribute for our example component. We have enabled COM+ security, specified that we wish to use PAS and RBS, and accepted the default levels for authentication and impersonation:

# C# 

using System;
using System.EnterpriseServices;

// Specify the file containing the key for assembly signing
[assembly: System.Reflection.AssemblyKeyFile("mykey.key")]
// Specify that we want a COM+ Server application
[assembly: ApplicationActivation(ActivationOption.Server)]

// Specify our application level security settings
[assembly: ApplicationAccessControl(
    // Enable COM+ Security
    Value=true, 
    // Enable PAS and RBS 
    AccessChecksLevel=AccessChecksLevelOption.ApplicationComponent,
    // Use the computer default values for authentication and impersonation
    Authentication=AuthenticationOption.Default,
    ImpersonationLevel=ImpersonationLevelOption.Identify)]

public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

Imports System
Imports System.EnterpriseServices

' Specify the file containing the key for assembly signing
<Assembly: System.Reflection.AssemblyKeyFile("mykey.key")> 
' Specify that we want a COM+ Server application
<Assembly: ApplicationActivation(ActivationOption.Server)> 

' Specify our application level security settings
<Assembly: ApplicationAccessControl( _
    Value:=True, _
    AccessChecksLevel:=AccessChecksLevelOption.ApplicationComponent, _
    Authentication:=AuthenticationOption.Default, _
    ImpersonationLevel:=ImpersonationLevelOption.Identify)> 

Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker
19.2.3.2 The ComponentAccessControl Attribute

The ComponentAccessControl attribute enables or disables COM+ security for an individual serviced component. This attribute accepts a single argument, which is set to true (to enable COM+ security for a component) or false (to disable COM+ access control for a component). The emphasized statements below demonstrate how we apply this attribute to enable COM+ security for our SecurityProTracker component:

# C#

// Enable COM+ Security for this component
[ComponentAccessControl(true)]
public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

'Enable COM+ Security for this component
<ComponentAccessControl(True)> _
Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

You must apply this attribute to every component that requires security services within a COM+ application. The ComponentAccessControl attribute has no effect if the ApplicationAccessControl attribute has disabled COM+ security at the application level.

19.2.3.3 The SecurityRole attribute

The SecurityRole attribute has a different affect depending on whether it is applied to an assembly, an interface, a class, or a method. In this section, we define some roles for our example component and use the SecurityRole attribute to apply COM+ RBS.

We define the following roles for our component. Figure 19-4 shows how we map these roles to the methods implemented by the SecurityProTracker component:

User

The User role can see the list of defects, but is unable to create new defects or close existing ones.

Tester

The Tester role is able to see the list of defects and to create new defects. Testers cannot close existing defects.

Developer

The Developer role is able to see the list of defects, and close defects. Developers are not able to create new defects.

Manager

The Manager role is able to view defects, create new defects and close defects.

Figure 19-4. Mapping roles to the SecurityProTracker component
figs/pdns_1904.gif

When the SecurityRole attribute role is applied to an assembly, it has the effect of defining the role within the COM+ application; we shall see the effect of this in Section 19.3. When used to define roles in this way, the attribute defines the properties described by Table 19-2.

Table 19-2. SecurityRole attribute properties

Property

Description

Role

Specifies the name of the property to define

Decription

Describes the role

SetEveryoneAccess

Adds the Windows Everyone group to the role

The SetEveryoneAccess property is worthy of special note; the SecurityRole can be applied to an assembly to define roles for a COM+ application, but the system administrator is responsible for assigning the COM+ roles to Windows user accounts. If the SetEveryoneAccess property is set to true, the Windows Everyone group will be assigned the role and all users will be able to access the methods protected by this role.

Use the SetEveryoneAccess property with care; it has the effect of assigning a role to all users, which may negate the benefits of COM+ RBS.

The following statement demonstrates how to define the User role by applying the SecuityRole attribute to an assembly, specifying a description and ensuring that the Everyone group is not assigned the role:

# C#

// Define the Users role
[assembly: SecurityRole("User", 
    Description="Users of the Security Pro product", 
    SetEveryoneAccess=false)]

# Visual Basic .NET

' Define the Users role
<Assembly: SecurityRole("User", _
    Description:="Users of the Security Pro product", _
    SetEveryoneAccess:=False)>

Applying the SecurityRole attribute to an assembly only defines the role and allows you to provide a description; it does not grant the role access to the methods defined by an interface or component. The emphasized statements below demonstrate how we define the roles for our example component:

# C#

// Define the Users role
[assembly: SecurityRole("User", 
    Description="Users of the Security Pro product", 
    SetEveryoneAccess=false)]
// Define the Tester role
[assembly: SecurityRole("Tester", 
    Description="Security Pro product testers", 
    SetEveryoneAccess=false)]
// Define the Developer role
[assembly: SecurityRole("Developer", 
    Description="Security Pro product developers", 
    SetEveryoneAccess=false)]
// Define the Manager role
[assembly: SecurityRole("Manager", 
    Description="Security Pro product managers", 
    SetEveryoneAccess=false)]

// Enable COM+ Security for this component
[ComponentAccessControl(true)]
public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

' Define the Users role
<Assembly: SecurityRole("User", _
    Description:="Users of the Security Pro product", _
    SetEveryoneAccess:=False)> 
' Define the Tester role
<Assembly: SecurityRole("Tester", _
    Description:="Security Pro product testers", _
    SetEveryoneAccess:=False)> 
' Define the Developer role
<Assembly: SecurityRole("Developer", _
    Description:="Security Pro product developers", _
    SetEveryoneAccess:=False)> 
' Define the Manager role
<Assembly: SecurityRole("Manager", _
    Description:="Security Pro product managers", _
    SetEveryoneAccess:=False)> 

'Enable COM+ Security for this component
<ComponentAccessControl(True)> _
Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

When applied to a component, the SecureMethod attribute grants a role access to all of the methods defined by a component. We want to grant the Manager role access to all of the methods defined by our example component, and we will apply the SecurityRole attribute to the SecurityProTracker class, as shown by the emphasized statements below:

# C#

// Enable COM+ Security for this component
[ComponentAccessControl(true)]
// Grant the Manager role access to the component
[SecurityRole("Manager")]
public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

<ComponentAccessControl(True), _
SecurityRole("Manager")> _
Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

We can grant roles permission to access individual methods by applying the SecurityRole attribute to individual methods in either the interface or the component class. If we apply the attribute to an interface method, the role will be able to access the method in any component that implements the interface. If we apply the SecurityRole attribute to a method in the component, the role is granted access to that component only, and not other components that implement the same interface. We will apply our access controls to the interface, as shown by the emphasized statements below:

# C#

public interface IDefectTracker {

    [SecurityRole("User")]
    [SecurityRole("Tester")]
    [SecurityRole("Developer")]
    void ViewAllDefects(  );

    [SecurityRole("Tester")]
    void CreateNewDefect(  );

    [SecurityRole("Developer")]
    void CloseDefect(  );
}

# Visual Basic .NET

Public Interface IDefectTracker

    <SecurityRole("User"), _
    SecurityRole("Tester"), _
    SecurityRole("Developer")> _
    Sub ViewAllDefects(  )

    <SecurityRole("Tester")> _
    Sub CreateNewDefect(  )

    <SecurityRole("Developer")> _
    Sub CloseDefect(  )

End Interface

When we apply multiple attributes, a user identity granted any of the specified roles will be granted access. We cannot require an identity to be assigned multiple roles in order to access a method or component.

19.2.3.4 The SecureMethod attribute

The final attribute to apply is SecureMethod, which enables per-method access control for a component. Without this attribute, COM+ RBS will be applied on a component level only, and the role protection we have assigned to the IDefectTracker interface will be ignored. The emphasized statement below demonstrates how we apply this attribute to our example class; this attribute defines no properties:

# C#

// Enable COM+ Security for this component
[ComponentAccessControl(true)]
[SecureMethod]
// Grant the Manager role access to the component
[SecurityRole("Manager")]
public class SecurityProTracker: ServicedComponent, IDefectTracker {

# Visual Basic .NET

<ComponentAccessControl(True), _
SecureMethod(  ), _
SecurityRole("Manager")> _
Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

19.2.4 Compiling and Installing the COM+ Application

In the previous section, we applied .NET attributes to configure COM+ RBS and PAS for a serviced component. In this section, we compile our example component and show you how to install a COM+ application. Our first task is to create a code file containing the statements we introduced in the previous section; we have listed the complete C# and Visual Basic .NET definitions of the example component below, and we have saved these statements into a file called Tracker.cs (C#) (see Example 19-1) or Tracker.vb (Visual Basic .NET) (see Example 19-2):

Example 19-1. Tracker.cs (C#)
using System;
using System.EnterpriseServices;

// Specify the file containing the key for assembly signing
[assembly: System.Reflection.AssemblyKeyFile("mykey.key")]
// Specify that we want a COM+ Server application
[assembly: ApplicationActivation(ActivationOption.Server)]
// Specify our application level security settings
[assembly: ApplicationAccessControl(
    // Enable COM+ Security
    Value=true, 
    // Enable PAS and RBS 
    AccessChecksLevel=AccessChecksLevelOption.ApplicationComponent,
    // Use the computer default values for authentication and impersonation
    Authentication=AuthenticationOption.Default,
    ImpersonationLevel=ImpersonationLevelOption.Identify)]

// Define the Users role
[assembly: SecurityRole("User", 
    Description="Users of the Security Pro product", 
    SetEveryoneAccess=false)]
// Define the Tester role
[assembly: SecurityRole("Tester", 
    Description="Security Pro product testers", 
    SetEveryoneAccess=false)]
// Define the Developer role
[assembly: SecurityRole("Developer", 
    Description="Security Pro product developers", 
    SetEveryoneAccess=false)]
// Define the Manager role
[assembly: SecurityRole("Manager", 
    Description="Security Pro product managers", 
    SetEveryoneAccess=false)]

// Enable COM+ Security for this component
[ComponentAccessControl(true)]
[SecureMethod]
// Grant the Manager role access to the component
[SecurityRole("Manager")]
public class SecurityProTracker: ServicedComponent, IDefectTracker {

    public void ViewAllDefects(  ) {}

    public void CreateNewDefect(  ) {}

    public void CloseDefect(  ) {}
}

public interface IDefectTracker {

    [SecurityRole("User")]
    [SecurityRole("Tester")]
    [SecurityRole("Developer")]
    void ViewAllDefects(  );

    [SecurityRole("Tester")]
    void CreateNewDefect(  );

    [SecurityRole("Developer")]
    void CloseDefect(  );
}
Example 19-2. Tracker.vb (VB.NET)
Imports System
Imports System.EnterpriseServices

' Specify the file containing the key for assembly signing
<Assembly: System.Reflection.AssemblyKeyFile("mykey.key")> 
' Specify that we want a COM+ Server application
<Assembly: ApplicationActivation(ActivationOption.Server)> 

' Specify our application level security settings
<Assembly: ApplicationAccessControl( _
    Value:=True, _
    AccessChecksLevel:=AccessChecksLevelOption.ApplicationComponent, _
    Authentication:=AuthenticationOption.Default, _
    ImpersonationLevel:=ImpersonationLevelOption.Identify)> 

' Define the Users role
<Assembly: SecurityRole("User", _
    Description:="Users of the Security Pro product", _
    SetEveryoneAccess:=False)> 
' Define the Tester role
<Assembly: SecurityRole("Tester", _
    Description:="Security Pro product testers", _
    SetEveryoneAccess:=False)> 
' Define the Developer role
<Assembly: SecurityRole("Developer", _
    Description:="Security Pro product developers", _
    SetEveryoneAccess:=False)> 
' Define the Manager role
<Assembly: SecurityRole("Manager", _
    Description:="Security Pro product managers", _
    SetEveryoneAccess:=False)> 

<ComponentAccessControl(True), _
SecureMethod(  ), _
SecurityRole("Manager")> _
Public Class SecurityProTracker
    Inherits ServicedComponent
    Implements IDefectTracker

    Public Sub ViewAllDefects(  ) Implements IDefectTracker.ViewAllDefects
    End Sub

    Public Sub CreateNewDefect(  ) Implements IDefectTracker.CreateNewDefect
    End Sub

    Public Sub CloseDefect(  ) Implements IDefectTracker.CloseDefect
    End Sub

End Class

Public Interface IDefectTracker

    <SecurityRole("User"), _
    SecurityRole("Tester"), _
    SecurityRole("Developer")> _
    Sub ViewAllDefects(  )

    <SecurityRole("Tester")> _
    Sub CreateNewDefect(  )

    <SecurityRole("Developer")> _
    Sub CloseDefect(  )

End Interface

We need to create the cryptographic key file that we specified with the AssemblyKeyFile attribute. We will create a test key pair for our example using the Strong Name Tool (sn.exe); consult Chapter 2 for details of signing assemblies. We assume that your DOS path includes the tools provided with the .NET Framework SDK; consult the .NET documentation for instructions. The following statement creates the key file we specified:

sn -k mykey.key

The following statements show how to compile the code file to create SecurityProTracker.dll, which is a library assembly with a strong name:

# C#

csc /target:library /out:SecurityProTracker.dll DefectTracker.cs

# Visual Basic .NET

vbc /target:library /out:SecurityProTracker.dll /reference:System.EnterpriseServices.
dll DefectTracker.vb

Now that we have created an assembly containing our component, we can use the .NET Services Installation tool (regsvcs.exe) to install the component on the local computer:

regsvcs SecurityProTracker.dll

The output from the .NET Services Installation tool is shown below:

Microsoft (R) .NET Framework Services Installation Utility Version 1.0.3705.288
Copyright (C) Microsoft Corporation 1998-2001.  All rights reserved.

Installed Assembly:
        Assembly: C:\SecurityProTracker.dll
        Application: SecurityProTracker
        TypeLib: c:SecurityProTracker.tlb
    [ Team LiB ] Previous Section Next Section