Security roles can be supported at the method, class, assembly, and interface levels. As you start to implement different levels of security, you will start to see the power of role-based security. Often in the development process, people look for ways to reuse components. One way to accomplish this is to use the same components but restrict access to portions of the information based on what role the users are in.
As an example, roles would be useful in an application used by stockbrokers. The application might restrict the type of transaction being processed, depending on whether the user is a stockbroker or a manager. Stockbrokers might have authorization to purchase only 1,000 shares of stock, whereas the managers might have an unlimited amount available to them.
Role-based security also can be used when an application requires multiple actions to complete the process. One example is a purchasing system that enables a customer representative to generate a purchase request but that allows only a supervisor to authorize that request, which then becomes a purchase order.
Take a look at Listings 15.1 and 15.2, which use role-based security, to get an idea of what is involved and where you might run into problems.
using System; using System.Reflection; using System.Windows.Forms; using System.EnterpriseServices; // The ApplicationName attribute specifies the name of the // COM+ Application that will hold assembly components [assembly: ApplicationName("RoleBasedApp")] // The ApplicationActivation.ActivationOption attribute specifies // where assembly components are loaded on activation // Library : components run in the creator's process // Server : components run in a system process, dllhost.exe [assembly: ApplicationActivation(ActivationOption.Server)] // AssemblyKeyFile specifies the name of the strong key // that will be used to sign the assembly. // The .snk file can be generated with sn.exe from the command prompt [assembly: AssemblyKeyFile("RolebasedKey.snk")] // ApplicationAccessControl is a COM+ security attribute that // enables and configures application-level COM+ security // The attribute maps to the Securities tab in a COM+ // application properties page [assembly: ApplicationAccessControl] namespace RoleBasedSecurity { // ComponentAccessControl enables security checking // at the component level. The attribute maps to the // securities tab in a component within a COM+ application [ComponentAccessControl] // SetEveryoneAccess(true) indicates we // we want the role to be populated with 'Everyone' when created [SecurityRole("EveryoneRole", SetEveryoneAccess = true)] public class MySecurityObject : ServicedComponent { public bool IsCallerInRole() { // Check if the user is in the role return ContextUtil.IsCallerInRole("SecurityAppDeveloper"); } public string GetCallerAccountName() { string ret = "Caller Unknown"; if (ContextUtil.IsSecurityEnabled) { SecurityCallContext sec; // get a handle to the context of the current caller sec = SecurityCallContext.CurrentCall; // get the current caller account name ret = sec.DirectCaller.AccountName; } return ret; } } } |
Imports System Imports System.Reflection Imports System.Windows.Forms Imports System.EnterpriseServices ' The ApplicationName attribute specifies the name of the ' COM+ Application that will hold assembly components <Assembly: ApplicationName("RoleBasedApp")> ' the ApplicationActivation.ActivationOption attribute specifies ' where assembly components are loaded on activation ' Library : components run in the creator's process ' Server : components run in a system process, dllhost.exe <Assembly: ApplicationActivation(ActivationOption.Server)> ' AssemblyKeyFile specifies the name of the strong key ' that will be used to sign the assembly. ' The .snk file was generated with sn.exe <Assembly: AssemblyKeyFile("RolebasedKey.snk")> ' ApplicationAccessControl is a COM+ security attribute that ' enables and configures application-level COM+ security ' The attribute maps to the Securities tab in a COM+ ' application properties page <Assembly: ApplicationAccessControl()> Namespace RoleBasedSecurity ' ComponentAccessControl enables security checking ' at the component level. The attribute maps to the ' securities tab in a component within a COM+ application <ComponentAccessControl(), SecurityRole("EveryoneRole", SetEveryoneAccess:=True)> _ Public Class RBSecurityObject ' SecurityRole configures a role named RbSecurityDemoRole ' on our component. SetEveryoneAccess(true) indicates ' we want the role to be populated with 'Everyone' when created Inherits ServicedComponent Public Function IsCallerInRole() As Boolean ' Check if the user is in the role Return ContextUtil.IsCallerInRole("EveryoneRole") End Function Public Function GetCallerAccountName() As String Dim ret As String = "Caller Unknown" If ContextUtil.IsSecurityEnabled Then Dim sec As SecurityCallContext ' CurrentCall is a static property which ' contains information about the current caller sec = SecurityCallContext.CurrentCall ' retrieve the current caller account name ret = sec.DirectCaller.AccountName End If Return ret End Function End Class End Namespace |
Listings 15.1 and 15.2 should give you a feel for how .NET role-based security can be used, but now take a look at a more real-life scenario.
Let’s say that you are building a web-based trading system for a financial firm. One of the issues that you run into first is how you will control security for different people using the system. Here is an example: A stockbroker has an assistant. The assistant needs to be able to add or modify information for clients. But the assistant is not allowed to place trades on the system; only the stockbroker is. This is where roles are really handy. Let’s take a look at an example in which the roles are not set up correctly for the situation just described (see Listings 15.3 and 15.4).
namespace TradingApp { // ComponentAccessControl enables security checking // at the component level. The attribute maps to the // securities tab in a component within a COM+ application [ComponentAccessControl] // SetEveryoneAccess(true) indicates // we want the role to be populated with 'Everyone' when created [SecurityRole("EveryoneRole", SetEveryoneAccess = true)] public class MyTradingObject : ServicedComponent { public void Buy(string symbol,int shares) { // Check if the user is in the role bool bl; bl = ContextUtil.IsCallerInRole("StockBroker"); if(ContextUtil.IsCallerInRole("StockBroker")) { //Allow the trade to go through } else { //Don't allow the trade } } public void Sell(string symbol,int shares, string accountid) { // Check if the user is in the role and if the account is his/hers if(ContextUtil.IsSecurityEnabled) { string acctname; if (ContextUtil.IsCallerInRole("StockBroker")) { SecurityCallContext sec; // get a handle to the context of the current caller sec = SecurityCallContext.CurrentCall; // get the current caller account name acctname = sec.DirectCaller.AccountName; //verify if this account belongs to this user //Do some code to check user name against account //if the account belongs to the user allow the user //to sell the stock } } else { //Don't allow the trade } } } } |
Namespace TradingApp ' ComponentAccessControl enables security checking ' at the component level. The attribute maps to the ' securities tab in a component within a COM+ application <ComponentAccessControl()> ' SetEveryoneAccess(true) indicates ' we want the role to be populated with 'Everyone' when created <SecurityRole("EveryoneRole", SetEveryoneAccess = true)> Public Class MyTradingObject Inherits ServicedComponent Public Sub Buy(ByVal symbol As String, ByVal shares As Integer) ' Check if the user is in the role Dim bl As Boolean If (ContextUtil.IsCallerInRole("StockBroker")) Then 'Allow the trade to go through Else 'Don't allow the trade End If End Sub Public Sub Sell(ByVal symbol As String, ByVal shares As Integer, ByVal accountid As String) ' Check if the user is in the role and if the account is his/hers If (ContextUtil.IsSecurityEnabled) Then Dim acctname As String If (ContextUtil.IsCallerInRole("StockBroker")) Then Dim sec As SecurityCallContext ' get a handle to the context of the current caller sec = SecurityCallContext.CurrentCall() ' get the current caller account name acctname = sec.DirectCaller.AccountName 'verify if this account belongs to this user 'Do some code to check user name against account 'if the account belongs to the user allow the user 'to sell the stock Else 'Don't allow the trade End If End If End Sub End Class End Namespace |
You’ll notice that these listings have implemented multiple levels of security based on which role the user is in. This gives you more of a granular approach to applying security to the components, which is great. Now let’s take a look at where all these information and configuration settings are maintained and controlled.