David Crawford, Dave McPherson
Contributors: Durga Prasad Sayana, Mei Wilson, Shawn Wu, Sudheer Mamidpaka, Sunil Gottumukala, Sunil Kadam, Chris Jackson, Eric Huebner
Microsoft Corporation
August 2006
Information in this document, including URL and other Internet references, is subject to change without notice. Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.
© 2006 Microsoft Corporation. All rights reserved.
Active Directory, Jscript, Microsoft, MSDN, Visual Basic, Visual C#, Windows, Windows NT, Windows Server and Windows Vista , are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.
The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
[This is preliminary documentation and is subject to change.]
Contents
Introduction to Developing Authorization Manager Solutions
Executive Summary
Application Authorization Challenges
Windows Authorization Manager
Application Security Design
Application Authentication Model
Determining an Application Authentication Model
Designing an Authorization Manager Solution
Identifying Resources and Operations
Determine Scoping Model
Determining Management Model
Implementing an Authorization Manager Solution
Store and Application Initialization
Client Context Initialization
Access Validation
Updating Policy
Performance
Environment-Specific Design Considerations
Deployment Considerations for Developers
Authorization Manager Availability
Deployment Approaches
Application Installation
Active Directory Storage
Using ADAM as a Store for Authorization Manager Policy
Authorization Manager Transactional and Concurrence Situation
Delegation of Administration
Troubleshooting the ASP.NET Authorization Manger Store Provider
Troubleshooting ADAM Access with Active Directory
Conclusion
Additional Resources
Introduction to Developing Authorization Manager Solutions
Executive Summary
Who Should Read This Paper
The intended audience for this paper includes architects, developers, technical decision makers, and consultants involved in application authorization design and implementation efforts.
Paper Overview
As corporate security and regulatory compliance requirements increase, the need and demand for a simple and common application authorization model grows. Today's application developers and enterprise administrators are aligning around Role-Based Access Control (RBAC) but are confronted by differing terminology and implementations. Authorization Manager provides a consistent and flexibility RBAC framework for Windows-based applications.
Authorization Manager is a role-based application framework which provides runtime access validation methods, storage, and a UI to manage access control. Authorization Manager is available on Windows Server® 2003, Windows XP, and Windows 2000 (runtime only.) Authorization Manager is an alternative to custom authorization solutions that tend to be limited in features, poorly integrated with the system, or very expensive to design and maintain.
Authorization Manager provides a high-end authorization solution for .NET applications and COM applications. Authorization Manager supports the use of Windows integrated, Active Directory® Application Mode (ADAM) authentication, Active Directory Federation Services (ADFS) claims aware applications, and SQL Server or custom authentication. The Authorization Manager runtime is separate from the authorization policy store, which may be stored in Active Directory, ADAM, or XML.
This paper assumes understanding of basic Authorization Manager concepts. For a conceptual overview of Authorization Manager and information on Authorization Manager administration and operations see: Role-Based Access Control for Multi-tier Applications Using Authorization Manager.
Authorization Manager benefits include:
- Intuitive Role Based Access Control (RBAC) Administration A simple common role-based administrative experience; administrators learn fewer authorization models and require less training.
- Natural Development Model Easy to integrate with native or managed applications; provides broad RBAC management and enforcement functionality.
- Flexible Authorization Rules Ability to define membership through dynamic Lightweight Directory Access Protocol (LDAP) queries or custom BizRules.
- Centralized Administration Multiple applications can be managed centrally and leverage common application groups.
- Flexible Storage Options Ability to store policy in Active Directory, Active Directory Application Mode (ADAM), and XML Files or SQL™ Server (on Windows Vista™ Beta 2.)
- Platform Integration and Alignment Support for platform features such as Active Directory groups, Windows security auditing, and Microsoft Management Console ™ (MMC). Assurance of proper integration of system access control objects such as the Windows access token and better alignment for future Windows access control features such as provisioning and entitlement engines.
- Reduced Software Development and Maintenance Costs Developers avoid the expense or trade-offs of custom access control. Authorization Manager does the expensive work of a full-featured authorization solution, including a complete RBAC model, policy storage (Active Directory, SQL Server, or XML), an MMC user interface, built-in application group support, rule and query support, integrated system auditing, and performance optimizations such as caching and late-binding.
- Enhanced Security Platform technologies are rigorously tested, broadly used and continually refined. A common RBAC model leverages administrators existing knowledge resulting in fewer access control mistakes.
Application Authorization Challenges
Creating custom authorization components can be costly, lead to a less secure solution due to bugs, and get little refinement due to tight budgets and timelines. Many custom developed authorization models go through a metamorphosis as the features grow. Each developer who works with such an application has the opportunity to change how authorization is performed. In some cases, each new developer or development team produces completely different models for the same application. This leads to inconsistent application management. Even using the ASP.NET IsInRole() capability can lead to unwieldy code with branch logic and multiple approaches coexisting in the same implementation such as demanding permission.
Additionally, in too many cases, administrators have little input on the administrative capabilities and the authorization management interface provided. They also have to learn how to use several models with varying results to an application. The lack of administrative consistency may lead to mistakes and greater attack surface for an application.
Authorization Manager provides a robust and common model that eases the burden on developers. It standardizes the task of authorization modelling and authorization checks within code and thus provides a common administrative model.
Windows Authorization Manager
Windows Server 2003 introduced Windows Authorization Manager while Windows 2003 Service Pack 1 extends its capabilities. Downloads are available for other Microsoft operating systems such as the back-ported Windows Authorization Manager Runtime for the Windows 2000 platform. Authorization Manager is available for Windows XP from the downloadable Windows 2003 Service Pack 1 Admin Pack. (For information about obtaining Authorization Manager, see Deployment Considerations for Developers.)
Using a role-based authorization mechanism is an attractive option to the basic authorization functionality of .NET for the enterprise developer because it offers the following:
- An intuitive RBAC model that allows configuration of role members and capabilities without requiring changes to applications.
- Centralized authorization policy for multiple applications.
- Security groups that you can create outside of Active Directory that an application administrator can manage.
- Dynamic group membership based on the result of a Lightweight Directory Access Protocol (LDAP) query.
Windows Authorization Manager utilizes a centralized policy store that holds authorization policy for one or more applications. Each application's policy is described as relations between role, group, task, and operations definitions.
Management of application policy can be delegated to application administrators as long as you choose Active Directory as the policy store location.
Windows Authorization Manager may establish role membership from the following:
- Active Directory or Local machine (SAM) accounts.
- Windows Authorization Manager Application groups.
- May contain Active Directory groups and other members.
- Deny Membership may also be based on group membership.
- Lightweight Directory Access Protocol (LDAP) queries on Active Directory or ADAM.
- Active Directory Application Mode (ADAM).
- Active Directory Federation Services (ADFS) claims.
An Authorization Manager LDAP Query Group is an application group that performs a runtime look up on a user's Active Directory or ADAM account object; the user is in the group if the LDAP query about the user, such as (title='auditor'), returns true.
Group membership and LDAP attributes are pieces of information that are often managed across the organization by identity management processes and technology. Application authorization through Authorization Manager may take advantage of this investment by conforming to the organizations security pattern reflected in the authorization policy store.
Application Security Design
Before integrating Authorization Manager, the application architect must complete the initial application security design and make architectural decisions such as the authentication framework selection. These decisions can be made based on application identity and security requirements. They are usually made independently of Authorization Manager considerations. This section provides information that will help you in this part of your design. After this security architecture is in place, you determine the application authorization model. Authorization Manager is designed to be flexible and will likely be able to be integrated with almost any authentication style you select.
Application Authentication Model
Application authentication options are typically driven by the type of user accounts being used. The user store selection (Active Directory, ADAM, ADFS, SQL Server, and so on) usually depends on business or organization requirements. Authorization Manager may be used for authorization with almost any authentication store, although non-Windows integrated authentication requires integration code for custom principals (described later in this paper), and administration using the Authorization Manager Microsoft Management Console (MMC) snap-in requires Windows identities. Administration for non-Windows identities is possible by writing a custom user interface using the Authorization Manager application programming interface (API). Developers often create these custom user interfaces integrated with the application management tools.
Authorization Manager can work with Windows integrated authentication types (in other words, those that result in a Windows logon token) at runtime, or ADAM authentication that yields Security Identifiers (SIDs), ADFS which issues claims, or even custom authentication types such as a SQL Server that could work similarly. The type of authentication chosen can imply features supported and integration requirements. For more information about choosing the appropriate authentication model, see the Microsoft patterns & practices site at http://msdn.microsoft.com/practices/ and the Security Guidance Center for Developers at http://msdn.microsoft.com/security/.
Determining an Application Authorization Model
Most applications that use Authorization Manager will use the trusted subsystem application model (also called the "protected subsystem" model) described below. Note that, while the two models imply distinct application designs, a particular application may choose to use a hybrid model that contains a trusted subsystem middle-tier design and use impersonation (with delegation) to maintain back-end auditing or to interface with legacy back-end resource managers.
Impersonation Model
Windows 2000 and earlier versions of Windows NT Server supported the impersonation model. In this model, server applications obtain a token for the connected client and impersonate the client token before attempting a secure operation, such as opening a file. The ACL on the file is compared to the user's token groups to determine whether or not that user has permission to open the file. The Windows Server 2003 family enhances support for the impersonation model through the Kerberos extensions of protocol transition and constrained delegation. Further information is available at Kerberos Protocol Transition and Constrained Delegation.
.gif)
Figure 1: Impersonation model and trusted subsystem model
Trusted Subsystem Application Model
Authorization Manager adds support for a trusted subsystem application model to the Windows security infrastructure. In the trusted subsystem model, the application server account only has enough access so that it can perform all the operations it exposes to clients. When a client requests an operation, the middle-tier application server authorizes the client request based on authorization policy that is specific to the application. If the client has permission to perform the requested operation, the server performs the operation on behalf of the client. In this model, clients do not have direct access to resources. While the impersonation model has its strengths and is appropriate in many cases, the trusted subsystem model has some advantages over the impersonation model.
Management of Permissions
In the impersonation model, each client's account is used to access the back-end resource, so the Discretionary Access Control List (DACL) of all resources must be maintained by granting each user the appropriate level of access. When the number of resources that are secured separately grows, especially when stored on separate back-end computers, management of the DACLs becomes more tedious. For example, you might organize resources into units that are secured in the same way, such as protecting all files in a sub-tree the same way or organizing users into groups with similar permissions.
In the trusted subsystem model, only the middle-tier service account is used to access the back-end resources; therefore, the DACLs can be simplified by granting only the service account sufficient access to perform all required operations on the objects. A service account should not be given full control rights or be a local administrator, but should have just enough permission to perform the applications complete set of operations. Application designers can document the level of permissions the application needs on back-end resources. This reduces the management of DACLs, because only the service account requires access to them. The service is given permissions to the resources, so you must trust that the service will not accidentally let one user access resources of another.
Rights Abstraction
With the impersonation model, assigning DACLs to resources to grant users the ability to perform high-level tasks can be difficult. High-level tasks such as Submit Expense or Query Inventory may require several operations on back-end resources. Rights abstraction is the process of determining precisely which low-level resource permissions are required for high-level application operations. It can be time consuming, particularly when many secured resource types are used.
In practice, administrators sometimes grant excessive access to resources to save time. A trusted subsystem application model allows the administrator to grant access only to the service account. Since the access to the resources is broadly given to the service account, determining the correct access is easier: usually the server gives just enough access to perform all operations that the server application may need (as defined by the application designer). The administrator does not have to define and maintain different levels of permissions on DACLs. The application developer defines the application operations in terms of the application tasks so the administrator does not have to translate the application tasks to permissions on back-end resources.
Connection Pooling
You often can achieve better scalability by connection pooling. Since the impersonation model maintains the user context through the request to a resource server, a separate connection to the resource server is usually required for each user request. If you create a connection for every client, you prevent reuse of a single connection to a resource server, such as a SQL Server database. Likewise, when using LDAP, multiple queries are possible on the same connection; therefore, maintaining a single open connection is often preferred for scalability. A trusted subsystem server application authorizes the client's request and accesses the remote resource for the client in the context of the applications service account. Since a new connection is not required, the application server can use the same connection with resource servers to service requests from different clients.
Controlled Access Points
Some applications enforce rules and workflow in the application server. In the impersonation model, each client is given appropriate access to each resource. Since the client has access directly to the resource, they could potentially access the resource though a method other than through the intended applications, which could cause undesired access and manipulation of resources.
In the trusted subsystem model, only the application service account has access to resources. This means that users cannot directly gain access to resources through a tool or API. In this model, the application server has more control over how the user sees and manipulates the resources.
Auditing
Auditing allows administrators to examine which users have attempted to gain access to particular resources. Auditing is most authoritative if the audits are generated by the same application that accesses the resources. The impersonation model does this by maintaining the user's context when it requests access on each system. This allows the remote system to authoritatively log the user and the requested access.
When you use a trusted subsystem application model, the back-end resource managers generate audits that log the server's service account as the account requesting access, not the client user on whose behalf the operation was performed. Authorization Manager provides runtime auditing when the access validation is performed (in other words, when the AccessCheck API is called), but these audits are generated at the application server. Because of this, mapping an access audit on a back-end resource to the user who made the request requires that the audit that logs on the application server be correlated with back-end audits. The application server audits can often be more informative than audits made on the back-end resource. From the application server, you can know more about the high-level task that the client is requesting, so a greater degree of the users intent can be recorded in the audits. On the back-end resource managers, only low-level operation requests can be audited, so it may be difficult to determine the user's higher-level activities.
Other Differences
In the impersonation model, the service account in which the application server runs may have little or no access to resources. To access resources on behalf of the client, the application server must impersonate the client's security context, which the application server can obtain when the client connects to it. When you limit the permissions of the server context, the server is limited to the permissions of the connected client when a successful attack takes place that compromises the server. If attackers want to gain broad access to back-end data, they must compromise the server and wait for users to connect. As each user connects to a compromised server, the access of the attacker grows in proportion to the connected users' permissions. If a highly-privileged account logs on to the server, such as the applications administrator, the attacker quickly gains permissions to the application data. If client users who have access to resources other than the applications log on to the server, the attacker also gains access to those resources because of the ability to impersonate the client to those resources.
A trusted subsystem server application starts out with a high degree of access to application resources so that, if the server application is compromised, the attacker has broad access to these resources. But since the client is not impersonated, the attacker is limited to the access of the service application's security context (which can be limited to the application's resources). While the attacker gains broad access to application resources, the attacker cannot access non-application resources.
Another concept to consider is that those organizations that attempt fine-grained security by increasing the number of groups in their directory and then checking those groups as role names within their applications may find broader implications to this practice. As the permissions within an application increase so does the number of groups that correspond to those permissions that get loaded as a SID for each group in the user token. As the number of groups increase so does the time to logon and to check that list against objects with DACLs. The impact of this "token bloat" is typically more noticeable with large organizations when there are a large number of users accessing a single system. This isn't to say that organizations shouldn't use groups. In fact, groups provide enormous benefits within organizations; however, there is an optimal use for groups and, in the case of the trusted subsystem model, Authorization Manager provides an ideal store for the fine-grain permissions while striking a balance with the common runtime environment.
Designing an Authorization Manager Solution
Identifying Resources and Operations
The first step in integrating Authorization Manager into an application is to identify the application operations that need to be authorized. An operation is a low-level permission that represents privileged actions or capabilities of an application. Operations should be created for each routine, query, method, and so on that comprises a discrete application function which requires access control. An operation alone may not be enough to perform a high-level task and may be required for more than one high-level task, but the operation itself is always executed as a unit and must be secured.
For example, an operation identified as ReadOrderInfo alone may not be enough for the high-level task Process Order, and the same routines may be needed for another high-level task, such as Query Order Status. Creating an operation for the set of routines that are required to perform each order activity allows finer permission specification. The more precisely the operations are defined, the more flexibility you have when managing permissions. However, operations that are defined too precisely make administration more complex.
For many resource managers, operations correspond to securable routines, procedures, or queries that operate on data or resources. The operations may be low-level and meaningful only to the application developer. To create permissions that are meaningful to administrators, the application developer can group low-level operations into Authorization Manager task objects. The graphic below depicts roles, — Employee, Manager and Administrator — with the arrows indicating the task groupings. The operations are grouped into the task to form the fine-grain permissions in the application.
.gif)
Figure 2. Operations and tasks
The following section will show where operations are viewed in the MMC, how they are created or defined (integers), how to create them programmatically in the store, and how they are used at runtime. Each Authorization Manager artifact will build from this low level concept.
Operations in the Policy Store
.gif)
Figure 3. MMC Interface Map to Code for Operations
The above operation could be written to the authorization store using the MMC UI or the following code snippet. The operation number is used at runtime in the code and the name is there to provide readability for the application developers who would group these into tasks for application administrators. Operations may be created by right-clicking the folder and adding the new operation through the context menu.
Programmatically Add Operations in the Policy Store
The following code will programmatically add an operation same as the above diagram into an Authorization Manager policy store. The operation ID is 3, which matches the MMC user interface Operation Number. This is useful for automating the creation of the policy store or application within. Policy store management tasks may all be performed programmatically and a more complete example may be found in the managed and script sample code at the end of this paper.
|
AzManOp = AzManApp.CreateOperation("ReadClaim")
AzManOp.OperationID = 3
AzManOp.Submit() |
The code below shows an access check (performed at runtime), which compares the client's context and determines ability to perform an operation. The result array's first index in this case would contain a true (S_OK = 0) or false (anything not equal to 0) value based on their role assignment. Most wrappers today transform the COM result to a managed Boolean type.
|
object[] result = (object[]) clientCtx.AccessCheck(
auditIdentifier, // The name used in auditing
internalScopes, // Scopes
operationIds, //operationIds[0] = myopid = 3
null, null, null, null, null // used for BizRules and Role filtering
);
|
Authorization Manager is flexible enough to be used in a similar manner to COM+ roles. A developer could write a wrapper and control access to a given method call handling security error messages or performing per item access control. This could be done by passing an operation ID for every field on an ASP.NET page into a single access check and using the Boolean array return values to set the visibility property of a set of user interface controls.
Determine Scoping Model
Using Scopes
Authorization Manager scope objects are collections of resources in which all resources of similar type have the same permissions. They can be simple collections such as "C:\My Documents", or more dynamic collections such as "*.doc", but for consistency applications should always present scopes as collections of application resources. Since each application exposes differing types of resources and different management abstractions, the application must take into consideration what scoping logic it will use and how it will be communicated to administrators.
A scope provides flexibility to the application in how to apply different access control to different resources. Depending on what the application is controlling access to, the collections may vary in form. For example, if an application performs operations on other computers, then a scope may represent collections of computer objects and be named something like "Computers in Sales Dept" or "Computers Running Windows XP." The primary requirement is that the application must be able to map a user request for access to a resource to the scope containing the resource when the access validation is performed.
Since scopes are defined by each application, and the application will map each access request to the appropriate scope, the use of the scoping model can vary across applications. While the Authorization Manager engine allows much flexibility in how the application wishes to integrate the scope, administrators must be able to understand the resulting abstraction. Therefore applications should be consistent in using the scope as collection of application resources and be careful to present it as such when using a custom UI.
General Scope Guidelines
The authorization administration should be clear and simple to manage. Scopes should not be abstract containers of resources that an administrator would not understand if the application is not automating it. Generally, somewhat static commonly used attributes of a resource, such as name or location, are understandable to administrators. When rarely used or when highly dynamic resource attributes (such as "last modified" or "Size") are used to group resources it is more difficult for administrators to visualize the resources contained in that group.
Scopes are easiest to understand if they are used consistently through the application. For example, if an application uses the path or location of the resource as scopes then it should not also use unrelated attributes (such as creator) for scopes; doing so will confuse administrators about which resources are in which scope and which scope has precedence.
It is simplest for the administrator when applications manage the creation of scopes and the administrator then creates and/or manages the roles within a scope. This way, the administrator does not have to know the application's scope name syntax and the application logic that is used to associate user access request with a scope.
When applications do allow administrators to create scopes, the application must provide documentation so that the administrator understands how to create scopes that are named correctly, since scopes are defined by each application and the application will map each access request to the appropriate scope. Requiring the administrator to create scopes adds complexity to both the application design and administration. The application must be designed to anticipate the creation of a scope and use the scope as appropriate, while the administrator must be able to understand how the application maps resources to scopes. If inheritance or overlapping is supported between scopes, then the administrator must understand the order in which the application will apply the scopes in granting access. For example, if a resource can be mapped to multiple scopes then the administrator must understand if the application will OR or AND the permissions granted by each scope a given resource maps to. Since the mapping of the resource requested to a scope and access enforcement is done within the application, the application will choose if and how to determine scope precedence.
Ideally applications should work to minimize the number of scopes (hierarchical or not.) BizRules can sometimes be used to employ generalized rules that allow data that would otherwise be in separate scopes to be managed in the same scope. For example, instead of making a scope for each user's mail box granting each user typical owner permissions, a rule could take the requestor's name and compare that to an attribute for owner on the mailbox object. Thus you don't need a scope per mail box.
Scoping example 1: Resource full-name to scope mapping
Many resource managers expose a management model that orients the authorization management process around the location of the resource. Typically file systems and Web servers are examples of such resource managers. In these resource managers administrators must understand the physical layout of the resources. Managing the authorization policy on the resource is done by locating the resource or a container of the resource and assigning permissions.
Mapping these resource managers into a scope model is straightforward. A scope can be directly mapped to a resource (file or URL) or a resource container (directory or VDir). Since these resource managers are typically hierarchical it is often necessary to provide a means of inheriting policy from parent containers to children containers. It is necessary to identify rules to determine the applicability and priority of multiple policies when policy can be specified at the resource, the resource container, and any ancestor containers of the resource. A common method of providing this inheritance and priority is through a longest path match algorithm that attempts to locate policy for a requested resource by looking for a scope object that matches the complete name of the resource including its path.
For example, a file system may have the following roles defined (Tasks simplified):
- Administrator: Can do anything to file.
- Author: Can create, read, and write to a file.
- Reviewer: Can read a file.
This may be in the following object hierarchy.
.gif)
Figure 4. Hierarchical scopes
A scope is defined as a collection of resources in which each resource of similar type has the same authorization policy. In this case the following scopes are defined.
| Scope Name | Contents | Administrator | Editor | Reviewer |
| A | Folder A and any non-scoped contents | Local Admins | | Local users |
| B | Contains the folder B and the file 1.doc | Local Admins | Bob | Kim |
| 2.xls | S3 = 2.xls (contains the 2.xls file) | Local Admins | Bob | Bob |
| *.doc | All files of type doc | Local Admins | | Bob |
Runtime scope mapping
At runtime a request is made for a specific file or directory (read, write, delete, list, and so on.). The file system must match the request to the correct scope. It can do this by first looking for a scope that exactly matches the full name of the resource. If a scope cannot be found, the algorithm looks for a longest prefix pattern match. Comparing the prefix of the requested file to the names of existing scopes (note that the algorithm can be smarter than this to support wild cards within a path such as C:\my docs\*.doc.) The algorithm continues to search for a scope until a scope is located or the root of the path is reached. If wildcards exist at any level, the algorithm attempts to match the wildcard at that level in the prefix search after checking for an exact match.
Scoping example 2: Resource attribute to scope mapping
Other resource managers hide the physical storage model from the administrators. Database applications, for example, commonly have schemas optimized for query performance and the physical layout of the data is not directly used for administration. These applications typically provide a management application which implements a unique management abstraction for the nature of the resources and the type of administrative tasks that are necessary.
An example of this non-location based scoping is an expense application in which an expense report is a resource. Each expense report is persisted as a row in a database. The management of expense reports is not done though a database UI but instead the application provides a UI that exposes management capabilities to users as appropriate based on their permissions. Actions can be performed on expense reports by the user who created them, the managers of that user, the auditors and reporters of the cost center to which the expense report is billed, and the administrators of the Expense Report application. In this application the management abstraction is to virtually collect expense reports based on their cost center attribute
The scoping may look like this:
.gif)
Figure 5. Attribute-based scopes
The following roles are defined:
- Submitter: Can submit and cancel an expense report.
- Approver: Can approve an existing expense report.
- Auditor: Can read existing expense reports.
- Administrator: Can read, delete, and approve existing expense reports.
The following assignments are made in each scope:
| Scope Name | Submitter | Approver | Auditor | Administrator |
| Cost Center 1000 | Dynamic group defined as user's whose costCenter attribute in Active Directory equals 1000 | Dynamic group that determines if the current user is in the chain of command of the person who submitted the report | Username of employees whose job it is to audit expenses | Usernames of Expense application administrators |
| Cost Center 1001 | Dynamic group defined as user's whose costCenter attribute in Active Directory equals 1001 | Dynamic group that determines if the current user is in the chain of command of the person who submitted the report | Username of employees whose job it is to audit expenses | Usernames of Expense application administrators |
| Cost Center 1002-2000 | Dynamic group defined as user's whose costCenter attribute in Active Directory is between 1002 and 200 inclusive | Dynamic group that determines if the current user is in the chain of command of the person who submitted the report | Username of employees whose job it is to audit expenses | Usernames of Expense application administrators |
Runtime scope mapping
Access verification is done through an algorithm that checks the costCenter attribute of the expense report being operated on and looks for a scope assigned to that costCenter. The scope may contain multiple costCenters in a numeric range. In this case the algorithm first checks for a scope name matching the cost center exactly and then looks for a scope whose name specifies a range that includes that of the target cost center.
Scoping example 3: Dynamic resource attribute scope mapping
A third example of scope mapping is a hybrid of the resource mapping example. An application management abstraction that collects resources based on the attributes of the resource and the attributes of the current user. This is done to allow a common permission template to be applied to many different resources, while the actual authorization may be different to each resource depending on a dynamic variable such as the identity of the person attempting to operate on the resource.
For example, an application that manages mail boxes for users may have a default permission set that each user gets to his or her own mailbox. Instead of creating a separate scope for each user, which requires more management and more storage for each scope, the common template can be implemented in a single scope called self. The self scope would contain roles granting the default permissions an identity called self.
The application may allow a user to override the default permissions. This can be done by creating a new scope for the mailbox with the permissions specified by the user. To allow administrator to provide access to overridden scopes, a common "management override" scope is maintained which grants permissions to managers.
Runtime scope mapping
When a user requests a mail box, the access validation is done by checking for a scope matching the specified mailbox; if one exists then an access check is done against that scope. If not, the algorithm checks to see if the current user is the owner of the mailbox and, if so, it does an access check for the requested operations in the "Self" scope. If at this point the requested access is not granted then the algorithm checks to see if the current user is a manger of the owner of the specified mailbox and, if so, it does an access check against the "Management Override" scope.
Scoping example 4: Work on Behalf
This illustrates the flexibility the scope introduces. The scope object can also be used to solve the problem of allowing one user to delegate some of their permissions to another user. To extend the above cost center scenario: a manager who is going on vacation or infant care leave may wish to delegate some of their capabilities to an interim manager. Or a director may wish to grant a subset of permissions in the expense application to an assistant.
To implement this, we can maintain Work on Behalf (WoB) scopes beside the existing costCenter scopes; one WoB scope for each user who is delegating permissions to another user as shown in the following figure:
.gif)
Figure 6. Work on behalf
To initiate the creation of such a Work on Behalf scope an e-mail is sent from the party who wishes to delegate permission to another party.
Within the scope the delegating party has the ability to define a custom role in terms of the tasks the delegating party is capable of and assign these roles to the users of their choice. The delegating party may create as many roles and assign as many users to them as desired.
Runtime scope mapping
For a user assigned to a WoB role to receive the access of the role at access validation time, they must specify whom they are working on behalf of. When access to an expense report is requested an access check is performed against the WoB scope of the user they specified, which validates the user has the specified access within the WoB scope. If the user has the requested access via the WoB scope, then another access check is performed against the applicable cost center scope (as done in the original scenario) to insure that the delegating user had access to give. To do this, a context for the delegating user is constructed (via something like InitializeClientContextFromName.)
Extensions to this scenario are to allow the application administrator to control the set of tasks (or roles) that a user may delegate to another user, and for the delegating user to specify a time limit on the membership of the delegate that will cause the delegate to be automatically removed from the roll on expiration.
Scoping Example 5: Query based scopes
Database applications that primarily expose data through queries (sometimes called "views") often control access to resources by permissions on the query objects instead of the tables or records in the database. In this case, views are created for each administrative and runtime task that the application exposes: for example, a view called "My Orders," which enumerates the order records whose owner attribute is that of a specified user, or a view for "NE division" orders, which enumerates all orders whose division attribute is set to North East.
Here access to the view implies access to each object returned in the view. This makes overlapping queries possible and easily managed. This also simplifies the programming model, which only needs to check access around the relatively small number of queries and not on each object. A drawback to this model is that if you want to know who has access to a particular object then you must execute each view (query), check for that object, and then enumerate the permissions for each view that includes the object. This will take more time than models where each object has a single policy specified; this will get worse as the number of objects and views grows. In some applications, checking the permission directly on a single object is common, so this model would be unacceptable. But in situations where the object's access policies are rarely examined per object, this model can simplify administration and development.
In such an application the following roles are defined:
- Sales Rep: Can create, read, update and delete their own orders.
- Sales Mgr: Can read update any order in there group.
- Auditor: Can read any orders.
- Administrator: Can read and delete existing orders.
The scopes are defined as follows:
- Scope 1 = View 1 = all orders submitted by a specific sales rep
- Scope 2 = View 2 = all orders in a specified group
- Scope 3 = View 3 = all non filled orders.
.gif)
Figure 7. Query-based scopes
Where each scope is assigned as follows:
| Scope | Sales Rep | Sales Mgr | Auditor | Administrator |
| View 1
Enumerates orders entered by current user | All sales reps.
(Runtime View takes current user name) | All managers
(Runtime View takes current user name) | Username of employees whose job is to audit orders | User names of order application administrators |
| View 2 | Sales Reps specified group. | Sales managers in specified groups. | Username of employees whose job is to audit orders in group | User names of order application administrators in group |
| View 3 | | Global managers | Username of employees whose job is to audit orders globally | User names of global order application administrators |
Runtime Scope Mapping
At runtime the app controls the presentation and orders are displayed as results in selected views. The user will select the view that corresponds to the activity they're performing based on the name of the query. The app will compare that name to a scope name and do a check access to see what the user can do to orders returned by that view. If the user has read access to the view, the app executes the view and lists the results. As the user attempts to update or delete an order, the app verifies the permission against the result from the original access check to see if update or delete is granted, the original access check can be used if the application enforces that the resource was within the view either explicitly by context. In this situation all permissions are managed at the view and not at the resource (the order record.) If the scope (view) of a particular resource is not implied or known by context, the access check on the resource must execute each view where each view that includes the resource identifies a scope. The application would decide if the requested permissions are needed in one of the set of scopes or in all (AND vs. OR.). Since executing all views (queries) at access check time will decrease performance, the application may optimize by making the set of views to which a resource applies an attribute of the resource. This would be the set of scopes containing the resource. This would require that when an object is created each view is executed to determine which apply to the new object. Each time an object is modified, views involving the modified attributes would be executed to determine if they apply and the object's views attribute would be updated. Likewise, when a view is created or modified, it would have to be executed to determine which objects it applies to. At administration time such filters could be more acceptable at runtime.
Determining Management Model
Defining tasks
The application should define operations for each routine that will run as a single unit and must be secured. While this allows the application to grant access more precisely, multiple operations may be required to perform a high-level task that the application exposes to the user. In this case, you should determine which operations are required to perform the high-level task and create an Authorization Manager task object to conceptually unify them as a task so administrators can treat them as a single permission. This requires some research on the part of the designer. Task objects are designed to simplify role management by providing meaningful, high-level permission sets that correspond to the application tasks that an administrator would grant a user permission to perform. Therefore, it is important for applications to install with a complete set of tasks required to control access for all uses of the application. The Role Definition and Modeling section, following, illustrates the use of task objects to combine low level operation permissions into high-level tasks.
Role Definition and Modeling
The Authorization Manager conceptual model prefers that a role is defined as a set of permissions at the application level (a role definition) and assigned as needed per scope (a role assignment). This attribute of the conceptual model prevents role bloat — a situation where different roles are created for each tuple of principal, permission, scope — while still providing for roles to be managed at the application level. Self-defined role assignments (what we've called role assignments that directly link to operations or tasks without a role definition while in scopes) makes application views harder. Therefore, it's recommended that each role be defined at the application layer.
The following describes a high-level authorization design for an application using Authorization Manager in an effort to bring some of the concepts together. Many applications run with classic Create, Read, Update, Delete also known as (C,R,U,D) operations.
Operations: Create, Read, Update, and Delete
.gif)
Figure 8. Operations view
The diagram above is the Microsoft Management Console (MMC) or AzMan.MSC view of managing policy stores. In particular, there are three XML stores shown; however, we are looking only at the AzStore.xml policy store that contains two applications, Expense and Contoso Insurance. We expand Contoso insurance while the MMC is in developer mode, and then expand the definitions node and select Operations Definitions where we currently view the four operations in the right panel.
Task: "Maintain Policy"
Contains Operations: Create, Read, Update, and Delete
.gif)
Figure 9. Maintain Policy task operations
In this case, we assign all operations to the Maintain Policy task.
Task: "View Policy"
Contains Operation(s): Read
.gif)
Figure 10. View Policy task operations
The View Policy task will only have the Read operation assigned to it.
Role Definitions:
Insurance Agent, Contains Task: Maintain Policy
Insurance Auditor, Contains Task: View Policy
.gif)
Figure 11. Role Definitions
This view shows the Role Definitions that contain the tasks previous assigned. These operations so far have been focused completely on the authorization structure or what the roles may do as far as the application. The next step is assigning users and groups to these roles. This is the point where there is opportunity to leverage the existing investment in the identity infrastructure.
Scope: Washington Policies
Roles assigned:
Agent: (assign to Washington agents)
Auditor: (assign to Washington auditors)
Scope: Oregon Policies
Roles assigned:
Agent: (assign to Oregon agents)
Auditor: (assign to Oregon auditors)
.gif)
Figure 12. Role Assignments
Once the user or group is assigned to a role, at runtime the application will pass the operation and scope to an AccessCheck function, which determines if the current user is assigned to a role that has permission to perform a request operation. If so, AccessCheck will indicate success; if AccessCheck cannot find an assigned role with the requested access, it will return failure and the application will then deny access accordingly.
There is another capability available for when a date/time or calling a function from a legacy application is required. Authorization Manager offers extensibility by calling custom code or runtime business rules. These custom code rules may be attached to role and task definitions. The following explains their use in greater detail.
BizRules
The Authorization Manager task object has the additional ability to dynamically qualify the permissions granted in a task, based on the result of an attached VBScript or JScript associated with the task. This allows for the access control decision to take into account runtime data, such as the dollar amount of an expense that has been requested, the inventory of a requested item, or the license status of the insurance agent attempting to view the insurance policy. BizRules are run during the AccessCheck call, and will run in the context of the Web application in the thread calling AccessCheck. If the BizRule returns success, the user receives the requested operations that were associated with the task. If the BizRule fails, the client is not allowed to perform the operations that are associated with the task. The user may be able to perform those operations through a separate task. Parameters are sent to the BizRule through the AccessCheck call, which takes corresponding name and value arrays as its parameters. For example, the first element in the varParameterNames array is the name corresponding to the first element in the varParameterValues array. The BizRule parameter's names array (varParameterValues) must be ordered accordingly so that names and values have the same index.
An application's authorization policy can have many tasks. Each task may or may not have associated BizRules, and new tasks and BizRules may be added by application administrators. At design time, application developers do not know if a call to AccessCheck will need to specify BizRule parameters (since BizRules may not be used for any given call to AccessCheck), so the application must send in the parameters potentially needed by BizRules. To facilitate BizRule developers, applications should define and publish the set of BizRule parameters that are sent into each AccessCheck call.
The type of data that AccessCheck should send as BizRule parameters includes information such as user name and restriction data. Restriction data may include items such as amount limits, account or ID numbers, or a user's manager. Any data that may be useful in determining whether or not to grant access at runtime can be used in each AccessCheck call and could be included in the applications list of published BizRules parameters.
BizRules require some scripting knowledge, and therefore it is not appropriate for most administrators to create and modify them. Usually, BizRules should be developed and shipped with the application or provided by the application vendor or other developers later. Since each BizRule that is run invokes the Windows Script Engine, BizRules should be computationally small tasks, such as comparing given parameters or querying a database.
Sample BizRules
The first sample utilizes JavaScript and the second VBScript.
This BizRule written in JScript insures that the time of day is between 9:00 and 17:00, using a 24-hour clock:
|
AzBizRuleContext.BusinessRuleResult = false;
dt = new Date(); hour = dt.getHours();
if (hour > 9 && hour < 17)
{
AzBizRuleContext.BusinessRuleResult = true;
}
|
This second sample is a VBScript BizRule that makes sure an amount that is passed into the BizRule as a parameter named ExpAmount is less than 500:
|
Dim Amount AzBizRuleContext.BusinessRuleResult = FALSE Amount =
AzBizRuleContext.GetParameter("ExpAmount") if Amount < 500 then
AzBizRuleContext.BusinessRuleResult = TRUE
|
When a BizRule refers to a parameter, AccessCheck caches the parameters, values, and result for a particular BizRule for the life of a client context. This allows improved performance in subsequent AccessCheck calls on the same client context that references the same BizRule with identical values. This means that, if BizRule results can change in subsequent AccessCheck calls without changing the BizRule parameters (as in the time-of-day BizRule described above), then the cached value may not be the correct value. For this reason, applications using BizRules should free the client context or call the IAzApplication::UpdateCache method periodically.
BizRules using .NET
Managed or .NET application developers may want to develop BizRule logic using managed code. The following shows how managed code within the application can be called from within a BizRule script. The first concept to understand is the COM callable wrapper.
Simulating COM Interfaces
The COM callable wrappercpconCOMCallableWrapper" (CCW) exposes all public, COM-visible interfaces, data types, and return values to COM clients in a manner that is consistent with COM's enforcement of interface-based interaction. For a COM client, invoking methods on a .NET Framework object is identical to invoking methods on a COM object.
To create this seamless approach, the CCW manufactures traditional COM interfaces, such as IUnknown and IDispatch. As the following illustration shows, the CCW maintains a single reference on the .NET object that it wraps. Both the COM client and .NET object interact with each other through the proxy and stub construction of the CCW.
.gif)
Figure 13. COM interfaces and the COM callable wrapper
In addition to exposing the interfaces that are explicitly implemented by a class in the managed environment, the .NET Framework supplies implementations of the COM interfaces listed in the following table on behalf of the object. A .NET class can override the default behavior by providing its own implementation of these interfaces. However, the runtime always provides the implementation for the IUnknown and IDispatch interfaces.
If the business rule exists as a .NET function in the process of the caller, the COM business rule will resolve to the function from the .NET code when executed. Make sure to add another class to your .NET application such as the following. (This works with .NET version 1.X but an explicit interface is required in .NET 2.0.)
.gif)
Figure 14. BizRules
From the Authorization Manager MMC, select the task definition, right-click and select Properties, click the Definition tab, and then click Authorization Script above the Authorization Rule dialog box.
.gif)
Figure 15. Managed BizRules
The above Visual C# pseudo code represents a class that could be added to an existing managed application that would perform a lookup function and contain data used in the BizRule. In this simple case, we make our function call in managed code and set a seed or arbitrary value of 200 in the constructor for this case. The m_DotnetBizRuleClass object retrieves a value from the user interface of the application (Amount.Text). The vbscript ("Amount") loads the value and runtime and is compared with the BizRule amount set in the policy store (500) as seen above. The DotnetBizrule class name and object are stored as objects in the access check. The value specified in the business rule at runtime is executed using the object that is in process, and then returned as a Boolean. The following shows how to use a business rule with .NET using the COM Callable Wrapper to access a .NET class running in process with the Authorization Manager COM objects.
|
try
{
azStoreOpen.Initialize(0, PolFile.Text, null);
app = azStoreOpen.OpenApplication2("MyApp", null);
clientContext = app.InitializeClientContextFromToken(0, null);
DotNetBizRuleClass m_DotNetBizRuleClass = new DotNetBizRuleClass("200");
m_DotNetBizRuleClass.setAmount(Amount.Text);
object[] oScopes = new Object[1];
oScopes[0] = null;
object[] oOperations = new Object[1];
oOperations[0] = 1;
object[] oInterfaceName = new Object[1];
object[] oInterfaceFlags = new Object[1];
object[] oInterfaces = new Object[1];
oInterfaceName[0] = "DotNetBizRuleClass";
oInterfaceFlags[0] = 0;
oInterfaces[0] = m_DotNetBizRuleClass;
object[] results =
(object[])clientContext.AccessCheck (
"TestApp",
oScopes,
oOperations,
null,
null,
oInterfaceName,
oInterfaceFlags,
oInterfaces);
bool bAuthorized = true;
foreach(int i in results)
{
if ( i != 0 )
{
bAuthorized = false;
break;
}
}
DebugMsgWithLine("Authorized: " + bAuthorized.ToString());
}
catch(Exception Excp)
{
DebugMsgWithLine("Exception Caught: " + Excp.ToString());
}
}
|
Implementing an Authorization Manager Solution
Server applications use the Authorization Manager runtime interfaces to manage connections to the authorization policy store and validate client requests. Although there are many possible environments and requirements supporting those environments when integrating Authorization Manager functionality, each application will follow the same high-level or common steps to validate client access requests.
Runtime Authorization Steps
The following steps are required in an Authorization Manager application. Each step is expanded in the following section.
- Store and Application Initialization Connect to the Authorization Store and open your application's corresponding application object.
- Client Context Initialization A user makes a request to access a resource. This is the point where claims or ADAM SIDs specific to the application user may be added.
- Access Validation Check access to a given operation (or scope and operation) using the AccessCheck method.
- Updating Policy In order to get updated, authorization policy applications must periodically update the cached policy store.
Store and Application Initialization
When the application runs, it initializes the Authorization Manager policy store using the IAzAuthorizationStore2::Initialize interface. To initialize the application, the process identity must have read access to the authorization store, but may not require write access to the store. To grant the application read or write access, use either the Authorization Manager Snap-In UI or call the IAzAuthorizationStore::PolicyReader or IAzAuthorizationStore::PolicyAdministrator methods.
After connecting to the store, the application calls IAzApplication2::OpenApplication to initialize an interface to its specific application within the policy store.
Initializing a store and application loads policy from the store, which will take some time depending on the size of the store. Most applications should initialize the store and application once at application initialization and cache the store object and application object for the life to the application.
Initializing an Authorization Manager policy store looks like the following in C# managed code:
|
AzManStore = new AzAuthorizationStoreClass();
AzManStore.Initialize(0, PolicyURL);
// 0 above is the default – modes are documented in MSDN
// Constants are available for the other modes e.g.
// AZ_AZSTORE_FLAG_BATCH_UPDATE often used for install but not access checks
// AzManApp = AzManStore.OpenApplication ("Application Name",null);
|
Note For the XML store, Authorization Manager reads in and caches all application policy when IAzAuthorizationStore::Initialize is called. The Active Directory and ADAM policy stores use a delay-load technique that allows initialization to proceed faster. Delay-load will load only store-level groups and application objects; it will defer loading objects within an application (operations, scopes, roles, tasks, and groups) information until the IAzAuthorizationStore::OpenApplicaion call is made. Similarly, IAzAuthorizationStore::OpenApplication will only load application level objects and scope objects. Child objects under each scope will not be loaded until a request is made (such as an AccessCheck call) for the scope's policy.
Client Context Initialization
When a client connects to the application, an Authorization Manager context must be created for the user.
To do this, use the following interfaces.
IAzApplication2::InitializeClientContext2 (new with Windows 2003 SP1), IAzApplication2::IntiializeClientContextFromToken,
IAzApplication2::IntiializeClientContextFromToken2 (for Windows 2000), IAzApplication2::AzInitializeClientContextFromStringSID, or the IAzApplication2::InitializeClientContextFromName method.
For performance purposes, whenever possible you should use a token to create a client context. A token is usually the result of a Windows logon and will contain logon information, such as whether it was an interactive or a network logon. Client context creation from a token is usually faster because it does not have to query a domain controller for group information. In rare cases, it is possible that role assignment may have been given depending on these logon properties, so whenever possible use the FromToken form of InitializeClientContext2 APIs. Using the InitializeClientContextFromName (or SID) method, an IAzClientContext2 object is created by looking up the user's group assignments in Active Directory. Since the user has not logged on, the context will not contain groups such as the NT AUTHORITY\INTERACTIVE group that identifies the logon type. In this case, if membership in a group or role is given to the interactive group, a user context that is created using InitializeClientContextFromName does not receive the membership. It is recommended that you do not use these logon property groups to secure resources or specify memberships in groups or roles, in which case InitializeClientContextFromName and InitializeClientContextFromStringSID can be used.
If you do use InitializeClientContextFromName or InitializeClientContextFromStringSID, you can add SIDs to the resulting client context via the IAzClientContext2::AddSids method.
The AzInitializeClientContextFromStringSID method creates an Authorization Manager context from a given SID in textual form. This behaves in a similar manner as the InitializeClientContextFromName method. When the AZ_CLIENT_CONTEXT_SKIP_GROUP flag is used, the AzInitializeClientContextFromStringSID method does not attempt to determine the group memberships of the given SID. The resulting client context only contains the specified SID. If the IAzAccessCheck method is called from this client context instance, role membership is only granted if the specified SID is used as a member of a role or group assigned to a role.
The InitializeClientContext2 method creates an empty Authorization Manager context. The interfaces added with Windows 2003 Service Pack 1 provide the interfaces needed to load SIDs and roles to this context (see IAzClientContext2::AddSids, IAzCleintContext2::AddRoles, and IAzCleintContext2::AddGroups). The new IAzCleintContext2::LDAPQueryDN property allows the access check to look up group membership from ADAM. ADFS also requires an empty context and, when an ADFS object is loaded with claims, they can be loaded into the Authorization Manager context for authorization.
Caching the Client Context
Many applications will benefit from implementing a cache of client context objects. If the usage pattern of your application is such that clients typically make multiple requests within a short time, then it is likely that caching the client context in your application will improve performance.
Review of Client Context Initialization Approaches
Empty InitializeClientContext2 is used for manual population scenarios such as ADFS and ADAM. When initializing an empty context the application will add the SIDs, roles, or groups to the context manually. The ADFS application typically populates roles and groups that were received from the ADFS claim transformation process, whereas with ADAM typically SIDs are populated.
Token InitializeClientContextFromToken uses the Windows logon token. All SIDs are in the token already; therefore, this is the fastest and preferred way to initialize a client context because no population of group SIDs from the domain controller is required.
SID InitializeClientContextFromSid is used when only the client SID is available. This is used in some tiered application environments as well as some store and forward scenarios. When using InitializeClientContextFromSid, pass the string SID of the Windows user, and Authorization Manager will perform the look-up on the domain controller and populate the SIDs to the context. Since these lookups are required, this mechanism is slower than InitializeClientContextFromToken.
Name InitializeClientContextFromName is used when only the name of the client is available. This is used in some tiered application environments as well as some store and forward scenarios. You can pass in the username, domain\username, or UPN and Authorization Manager will populate SIDs and groups for you. Using the username alone (without a domain specified) can result in an incorrect client context if the same username is used in a trusted domain. Since these lookups are required, this mechanism is slower than InitializeClientContextFromToken.
The following is a typical managed code approach for initializing client context.
|
// Have AzMan use the process token
// IAzClientContext ctx = app.InitializeClientContextFromToken(null, null);
////////////////////////////////////////////////////////////////////////////////
// another approach to grab a token is from a WindowsIdentity or IIdentity Interface
WindowsIdentity wid = (WindowsIdentity)User.Identity;
IntPtr tokenhandle = wid.Token;
IAzClientContext ctx =
app.InitializeClientContextFromToken((ulong) tokenhandle, null);
/////////////////////////////////////////////////////////////////////////////////
//By token again…
IntPtr tokenhandle=WindowsIdentity.GetCurrent().Token;
IAzClientContext ctx =
app.InitializeClientContextFromToken((ulong) tokenhandle,null);
// By Name…
IAzClientContext ctx= app.InitializeClientContextFromName(userName,dom,null);
// Retrieve a name via WSE (a token could be utilized using Kerberos)
// There are a few possibilities in the web services side.
userName = MapSoapContextToUser(RequestSoapContext.Current);
///////////////////////////////////////////////////////////////////////////////////
// Retrieve the token at the ASP.NET early in the ASP.NET Execution Chain
// On Authentication would be a good place…
HandleRef token = new HandleRef(
new object(),
((HttpWorkerRequest)((IServiceProvider) HttpContext.Current).GetService(
typeof(HttpWorkerRequest))).GetUserToken());
// The following demonstrates and AZMAN_APP object store in the ASP.NET Application
// space
AzManClientContext =
((IAzApplication)Application["AZMAN_APP"]).InitializeClientContextFromToken(
(UInt64)token.Handle, 0);
//
// Save the client context in a session variable
//
Session["AZMAN_CLIENT"] = AzManClientContext;
////////////////////////////////////////////////////////////////////////////////////
// ADFS, ADAM : EMPTY
// – Empty context building – Add Groups, Roles
// Typically Add Sids with ADAM
_azApp = _azStore.OpenApplication2(_application, null);
_azContext = _azApp.InitializeClientContext2(_application, null);
|
Access Validation
After a client context is created, applications are ready to validate the client's access to a requested application resource. For an Authorization Manager application, this can be done in two ways.
Access Check
When the user makes a request, the application maps the request to the required operations and determines the scope in which the user is making the request (as discussed in the Determine Scoping Model section of this paper). Then the application calls AccessCheck and sends the following parameters:
String for security event log audit records This is a string that will give the audit administrator more information about the security event. For example, you could put the name of a high-level application activity such as "Submit-Expense" here to inform the audit administrator of the high-level activity that the user is performing.
Scope array This is an array of scope names. In version 1, Authorization Manager only supports one scope name and it must be in the first element of the array. The application maps the requested resource to the correct scope based on the scoping logic the application implemented.
Operation IDs array This is an array of operation IDs. The application must map the requested action to one or more operations. Each operation has a corresponding ID value. Each value is passed in as a separate element of the operations array.
BizRule Parameter names array (optional) BizRule parameters are passed to AccessCheck via two arrays. The first is the BizRule parameter names array. In this array you must send in each BizRule parameter name (a text string) as a separate element. This must be alphabetized in case insensitive ASCI order. For example, BizRule parameters with names starting with a letter at the beginning of the alphabet come before BizRule parameters with names that start with a letter near the end of the alphabet. The BizRule names array must correspond (index to index) with a value in the BizRule Parameter values array.
BizRule Parameter values array (optional) This is an array of values corresponding to the name specified in the BizRule Parameter name array. Each BizRule parameter value must be placed in an array corresponding to the BizRule Name for each value.
BizRule COM interface names array (optional) If you want to use code in the application and call it from a BizRule (useful if you want to do more complex computation in BizRules) you can pass in the COM interface names and values to AccessCheck and call them from the BizRule. The BizRule COM interface name parameter works similarly to the BizRule Parameter names array. It is an array of names that will be used in the BizRule to reference the COM interface implement by the application. The BizRule COM interface name array must correspond, index to index, with the BizRule COM interfaces value array.
BizRule COM interface values array (optional) This is an array of interfaces corresponding to the name specified in the BizRule Parameter name array. Each BizRule parameter value must be placed in an array corresponding to the BizRule Name for each value.
The results of the AccessCheck are returned from the AccessCheck method call in the Results array. Results are given per operation requested in the operations array specified in the AccessCheck call. To determine if access has been granted, analyze the value in each element of the Results array corresponding to each operation you requested.
Sample code using AccessCheck is provided in the Windows SDK. Search on Microsoft.com for information about downloading or obtaining the Windows SDK. Samples are added or updated on each release of the SDK.
Role Query and Authorization
After a client context is created, applications have the option of querying role information about the user in order to present a user interface based on the user's role memberships. For example, a Web-based application may render a Manager user interface for people in the Manager role, and an interface based on the employee role for everyone else. The IAzClientContext2::GetRoles method enumerates the user's role memberships. The set of roles that is returned from GetRoles is based only on explicit membership assignment; GetRoles does not evaluate BizRules. This function is often used to mimic the IsInRole capability of the built-in principal but at the loss of pivot ability.
Additionally, applications that use scopes may wish to enumerate the scopes in which the client has been assigned any access. Applications that have a capability-based user interface, or need to query the client's permissions across the application for reporting or exporting to another tier can use the IAzClientContext2::GetAssignedScopesPage method. If more granularity is needed after enumerating the assigned scopes, the application can call IAzClientContext2::GetRoles for each assigned scope. Likewise, from each role, tasks and operations can be enumerated if needed.
Updating Policy
In order to receive policy changes, the application must have a periodic call to IAzAuthorizationStore::UpdateCache. The IAzAuthorizationStore::UpdateCache method will check to see if a policy change has taken place since the policy in memory was last refreshed. If no change has taken place, no policy is updated. If the policy has changed, the IAzAuthorizationStore::UpdateCache method will reload the policy differently on the XML store than on the Active Directory and ADAM stores. The XML store will be completely reloaded into memory. For the Active Directory and ADAM stores, which use the delay-load technique discussed above, IAzAuthorizationStore::UpdateCache will refresh only the applications and scopes that have been previously opened. Over time an application may load many scopes, and it is possible that calling the IAzAuthorizationStore::UpdateCache will result in a delay while each application and scope in memory is refreshed. Applications have the option of occasionally closing and reopening the application (by calling the IAzAuthorizationStore::CloseApplication and then IAzAuthorizationStore::OpenApplication method) to free all policy in an application. This will result in faster calls to IAzAuthorizationStore::UpdateCache. However, doing this will result in a delay on the next call to access check to load the policy of the specified scope. Applications that will have large numbers of scopes should periodically call IAzAuthorizationStore::CloseApplication or expose configuration options allowing an application administrator to configure the frequency of IAzAuthorizationStore::UpdateCache as well as closing and reopening the application.
Performance
Application initialization and access validation performance can vary, depending on how applications use features such as XML storage, BizRules, and Authorization Manager application groups. Application designers have several options to leverage in meeting performance requirements.
Application Initialization Performance
Initialization performance for large stores improves when you use the Active Directory or ADAM store rather than the XML Authorization Manager storage provider. When an application initializes a connection to an Authorization Manager policy store, authorization policy is loaded into the address space of the application. The amount of policy that is loaded depends on the store type.
The Active Directory or ADAM Authorization Manager policy store allows Authorization Manager application objects to be loaded and unloaded on demand and the Authorization Manager scope objects to be loaded only when needed.
The XML Authorization Manager storage provider loads the entire authorization policy into memory.
Additionally, the time it takes to initialize increases as the number of objects within the Authorization Manager policy store increases or as the number of members in Authorization Manager application basic groups increases. To minimize the size of application groups, use Active Directory groups when possible, and then use these Active Directory groups in Authorization Manager application groups or roles.
See Updating Policy in the previous section for information on how to improve policy updating performance.
Important Authorization Manager groups are not meant to replace Active Directory groups. They provide a solution to application administrators who want some control of groups within the application due to the latency or political issues often associated with managing Active Directory groups in large organizations. It's recommended that application groups are unions, concatenations, or subtractions of Active Directory groups and ideally < 1000 members. For groups with higher numbers of members, applications should use normal Active Directory group memberships.
Access Validation Performance
Access validation includes the two-step process of initializing a client context and calling AccessCheck (or a query such as GetRolesForUser.) To improve performance in access validation you can first look at ways to optimize the client context initialization step, and then examine how to optimize the calling of AccessCheck.
The first step in improving the performance of initialization of client context is to only do it when you have to. Many applications may have expected usage patterns that would benefit from caching the client context. Additionally, using the token-based or manually created client context initialization mechanisms when possible can help improve performance. See Client Context Initialization in the previous section, for information about using the best client context initialization method and caching the client context.
An important thing to remember is that you do not need to initialize a connection to the Authorization Manager policy store and open an application on every client request. Store initialization should be done only once at application initialization; while OpenApplicaion is done once or rarely depending on the policy updating method you use (See Updating Policy for information on choosing a policy updating approach.)
Understanding the Authorization Manager AccessCheck method provides insight into how to optimize access validation performance.
The AccessCheck method performs the following steps:
- Identifies scopes that apply to the AccessCheck routine, both within the specified scope and the global scope.
- Enumerates the set of roles in the scopes from step 1.
- Discards roles that do not refer to requested operations. If RoleForAccessCheck is specified, all roles other than that specified are ignored.
- Verifies the client security context's membership in each role.
- If the client is a member of the role in step 4, AccessCheck records all requested operations that are assigned to the role as granted.
- Repeats steps 3-5 for each role in applying scopes until all requested operations are granted.
The above logic is repeated a maximum of three times.
On the first pass, AccessCheck ignores any tasks containing BizRules and application groups containing LDAP queries. Both BizRules and LDAP queries can take a long time to compute and can require network operations, so the first pass takes place in case they are not needed.
In the second pass, AccessCheck processes BizRules, but still ignores LDAP query groups. BizRules are supposed to be simple and may not involve network operations, so they are likely to be faster to process than LDAP queries.
The third pass processes all data, including LDAP queries, using the cached results from previous iterations. AccessCheck terminates as soon as access is granted to all of the requested operations.
Since AccessCheck is optimized to evaluate static memberships ahead of dynamic memberships, and tasks without BizRules ahead of tasks with BizRules, you can optimize performance by using static assignments where possible.
Authorization Manager application groups are not evaluated until AccessCheck is called and membership in a role is queried. (This process of group evaluation is also called "late-bound.") For LDAP query application groups, once membership in an application group is established for a user, that user's membership is cached for the life of the user context object or until IAzAuthorizationStore::UpdateCache is called. Because of this, subsequent AccessChecks that involve group evaluations speed up as group memberships are resolved and cached. Caching is done on a per AzAuthorizationStore basis. Calls on multiple threads will result in multiple or distinct caches.
All caching is removed when the last reference to a store is gone.
As a best practice, BizRules should be kept computationally small. BizRules offer the greatest degree of flexibility in an AccessCheck call, and therefore offer the greatest potential to decrease performance. Network bound BizRules decrease the performance of AccessCheck. Often this cannot be avoided — for example, if a database needs to be queried to check a credit history (but such queries will delay the result to the user).
Environment-Specific Design Considerations
Autho