Sometimes, administrative role-based security it not granular enough for the task at hand. Consider a situation in which your application maintains a private resource (such as a database) that does not expose any public interfaces directly to the clients. You still want to allow only some callers of a method to access the resource and deny access to other callers who are not members of a specific role. The second (and more common) situation is when a method is invoked on your object and you want to know whether the caller is a member of a particular role so you can better handle the call.
To illustrate the second situation, suppose in the bank example, one of the requirements is that a customer can transfer money only if the sum involved is less than $5,000, whereas managers and tellers can transfer any amount. Declarative role-based security goes down only to the method level (not the parameter level) and can only assure you that the caller is a member of at least one of the roles you have granted access to.
To implement the requirement, you must find out the caller’s
role programmatically. Fortunately, COM+ makes it easy to do just
that. Remember that every method call is represented by a COM+
call
object (discussed in Chapter 2). The call object
implements an interface called
ISecurityCallContext
, obtained by calling
CoGetCallContext( )
.
ISecurityCallContext
provides a method called
IsCallerInRole( ),
which lets you verify
the caller’s role membership. IsCallerInRole( )
, is available on
IObjectContext
, a legacy from MTS as well. Example 7-1 shows how to implement the new requirement
using the call object security interface.
Example 7-1. Verifying the caller membership by calling ISecurityCallContext::IsCallerInRole( )
STDMETHODIMP CBank::TransferMoney(int nSum,DWORD dwAccountSrc,DWORD dwAccountDest)
{
HRESULT hres = S_OK;
ISecurityCallContext* pSecurityCallContext = NULL;
_bstr_t bstrRole = "Customer" ;
VARIANT_BOOL bInRole = FALSE;
hres = ::CoGetCallContext(IID_ISecurityCallContext,
(void**)&pSecurityCallContext);
if(pSecurityCallContext == NULL)
{
//No security call context available, role-based security not in use
return E_FAIL;
}
hres = pSecurityCallContext->IsCallerInRole
(bstrRole,&bInRole);
pSecurityCallContext->Release( );
if(bInRole)//The caller is a customer
{
if(nSum > 5000)
return E_FAIL;
}
return DoTransfer(nSum,dwAccountSrc,dwAccountDest);//Helper method
}