Entitlement engines are infrastructure component-enabling software applications that restrict access to data, based on users' privileges. They operate by comparing privileges granted to users (keys) with access permissions that protect resources (locks).
The need for entitlement engines originates from the harsh reality that users are created unequal and different. Access to data is granted based on the business relationship between the data provider and supplier. For example, a Citibank customer should be granted access to data relevant to his or her own account and service level agreement - and only to that data.
Historically, client/server systems relied on RDBMS's built-in security mechanism for privilege checks. To do so, each user had a database logon. The transition to Web-based three-tier systems mandated an entitlement check mechanism that was independent of RDBMS's privilege checks and capable of handling Web-based access. Furthermore, portals and search engines that aggregate data from multiple enterprise legacy systems mandate a centralized control of entitlement checks and security.
This article provides blueprints for building a centralized entitlement engine for an enterprise. It addresses the major design and methodology issues in building such an entitlement engine and provides some real-world experience. The article is geared to architects, developers, and security professionals in need of managing access to multiple enterprise systems.
Additional information about building a centralized entitlement engine is available at http://home.nyc.rr.com/giora/, and includes the following:
Evolution of Entitlement Engines
- Project plan
- Integration with other systems
- Management API
The evolution of entitlement check engines can be roughly broken into the following stages:
The transition from RDBMS-based entitlement checks to custom-built entitlement engines was driven by the stateless nature of Web clients using the HTTP protocol. It made little sense to perform a database logon for each HTTP request. Doing so would have been slow and resource-intensive.
- Client/server systems relied on RDBMS's built-in features to perform entitlement checks. (This mechanism was also used by PERL-CGI Web-based systems.)
- Early Web-based systems relied on a custom-built entitlement check API servicing a single application.
- Newly built enterprise applications rely on enterprise entitlement engines capable of centralizing the management of entitlement company-wide.
In the same way, portals and search engines aggregating data from multiple applications across an enterprise drive the transition from custom-built entitlement engines to a centralized entitlement engine.
A List of Requirements
The following is a list of mandatory requirements for an enterprise entitlement engine:
One centralized point of management and storage for entitlement data - to make a long story short, this is the mother of all requirements. It mandates a single database containing all entitlement information. By centralizing the data and its management, it makes the life of security administrators manageable.
Users must be uniquely identified company-wide.
All enterprise systems supported by the entitlement engine must comply with this previously mentioned user identification mechanism.
Support integration with multiple systems in multiple languages and environments - a language-independent API.
Provide high performance.
Allow dynamic changes of entitlement policies.
Allow management of entitlement, based on assignment of users into groups. And allow management of entitlement based on assignment of groups into groups.
Support assignment of unique identifiers to enterprise resources in need of entitlement checks (hyperlinks and APIs).
Capable of entitling hierarchies of protected resources.
Hierarchical management model must support the delegation of management tasks to part of the protected resources hierarchy.
Intuitive manageability via usage of a GUI Console
Build vs Buy
The project plan (found at http://home.nyc.rr.com/giora/) demonstrates that building an enterprise entitlement engine can last between three to six months. In an ideal world, an entitlement engine is something we buy from a software vendor (less code for us to write and maintain). I suggest you find a vendor that satisfies the conditions mentioned in the preceding requirement list. Before you write a check, however, read the following warning: Beware of slow entitlement engines!
Yes, performance is the number-one requirement. We can't cut any slack when it comes to performance. A delay in response time due to slow entitlement checks is likely to have a negative influence on the career of the engine owner.
A common characteristic of the high-performance engines is that they all do all of the runtime entitlement checks in memory. I think an entitlement engine that needs to query databases, move entity beans between pools, or any other type of roundabout configuring in runtime is doomed!
Now you know what to look for when you shop for an entitlement engine. If you find a good one that suits your needs, more power to you. If you don't, read the following for a blueprint of a custom-built entitlement engine.
Use Case: The Actors
- An application owner interested in protecting the hyperlink to his or her applications
- A security administrator in charge of the entitlement engine
- An application user interested in using the application (see Figure 1)
Use Cases by Chronological Order
List of users or groups that will be granted management right for this application
Name of the application in conformance with the corporation naming convention - this name will be used later to fit the application within the ProtectedItem tree (covered later)
Type of protection needed, whether external or internal (external protection entitles access to a URL. Internal protection entitles access to a block of code of an application)
The security administrator provides the application owner with the necessary rights to manage the application using the entitlement engine management console.
1. The first use case to take place happens when the owners of an application contact the entitlement engine administrator to notify him or her that the application needs entitlement protection. To get started the application owners must provide the following information about the application:
2. The second use case to take place is driven by an application owner who does the following:
Protects hyperlinks via external entitlement check.
Protects APIs via internal entitlement check.
Grants privileges to users and groups to use the hyperlinks and APIs. Privilege/entitlement management is done via the entitlement engine console.
3. The last use case is the actual entitlement/privilege check. A user of the previous protected application attempts to access the application. The call is delegated to the entitlement engine. The entitlement engine performs the entitlement check. If the check result is a success, access is allowed to the protected application (see Figures 4-8).
EntitleableEntity: Represents an entity capable of granting entitlement and managing entitlements or benefitting from them.
The enterprise entitlement engine object model (see Figure 2) consists of the following:
User: The user object represents a real-life client/user (actor in UML terms) of an application protected by the entitlement engine. Users have one unique user identifier: GUID.
Group: A collection of users or groups; groups can be granted rights to access applications or manage entitlements via their relationship to their super class (EntitleableEntity).
ProtectedItem: Represents a hyperlink or an API of an application. The ProtectedItem is organized into a hierarchical tree (covered later).
Rule: Represents a rule associated with the privilege. The Rule class holds a user-defined rule written in some rule language. (Rules are optional.)
Action: Represents an operation to be performed on the protected item, such as view, execute, modify, delete, manage, deny view, deny execute, or deny manage.
Privilege: A single association between protected resource, user or group, action, and rule, it determines the usage rights of users on protected applications.
PrivilegeOwnership represents the relationship between the Privilege and the exact EntitleableEntity (User or Group) to which it was granted.
CombinedPrivileges represents the cross influence between a privilege and all of its beneficiaries - users who got the privilege directly or indirectly via group membership. There's a good reason to build this relationship into the model. Privileges do change during the runtime of the entitlement engine. When they change, the ripple effect on each user must be figured out. This relationship makes it possible.
The managing relationship is a sticky issue. Remember, the burden of entitlement management between multiple administrators should be distributed.
-We need a separate hierarchy to determine who can manage what.
-All management operations must be entitled.
The members relationship between a group and EntitleableEntity represents a group membership. Notice the potential for recursiveness, where a group can contain another group. Also notice that circular relationships between groups are legal.
Missing Items in the Object Model
What about supporting a use case where access is revoked for all users of a given company? Our object model doesn't cover that. Instead the combined usage of GUID and a cross-reference table enables integration between an entitlement engine and any CRM system.
I'll use a real-life example to demonstrate a fundamental part of our object model: the ProtectedItem tree. The example is entitlement management for a multinational bank operating in the U.S., Europe, and Asia. The bank has the following departments: institutional banking, private banking, and investment management. Each department has several systems. To distribute the burden of security management, each department employs its own security administrator. The centralized entitlement engine owner delegates management rights to the regional security administrators. The delegation is done by assigning regional security administrators with full control over a portion of the entire ProtectedItem tree.
The security administrator employed by the Asian institutional department has full control over the tree branch com.abcbank.Institutional.Asia. He's entitled to manage any tree branch below that point (see Figure 3).
In a similar manner a user granted access to com.abc
bank.Institutional.Asia is implicitly granted access to com.abcbank.Institutional.
Asia.MoneyMarkets, com.abc bank.Institutional.Asia.Swift,
Note: The dot separator is used to break down the names into a hierarchy.
The Runtime API
The following API contains all the necessary signatures for privilege/entitlement checks. The mapping of this API to various languages will be discussed later.
public boolean checkEntitlement(User user,
The runtime operation of the entitlement engine is demonstrated by Figures 4-8.
ProtectedItem item, Action action, Rule, rule);
public boolean checkEntitlements(User user, ProtectedItem items,
Action actionsRule rules);
1. A user clicks on a URL associated with a ProtectedItem in the ProtectedItem tree (see Figure 4).
2. An HTTP server passes a request to a custom-built plug-in designed to package requests for the entitlement engine API. The plug-in forwards the request to the entitlement engine API for an entitlement check (see Figure 5).
3. The entitlement engine looks up the user ID in its user privileges' cache and fetches the collection of privileges for that user (see Figure 6).
4. The privilege list is searched for all privileges referencing the Protected-
Item hierarchy with the relevant action (see Figure 7).
5. In case such privilege is found and access is not denied, the entitlement engine returns success; otherwise, it defaults to a failure (see Figure 8).
Note: We're making an assumption that by the time the HTTP server entitlement engine plug-in receives the HTTP request, it contains the GUID of the user. Implicitly it's assumed the user had already been authenticated and the GUID was fetched from another system.
The Game Plan
Implementing an entitlement engine can be confusing for the newcomer. Have no fear: soon you will understand both the forest and the trees. The entitlement engine code can be broken into the following pieces:
The following walks you through the architectural issues encountered when building a high-performance entitlement engine.
- Persistent datastore holding configuration information
- Bootstrap logic loading the users' privilege mapping into memory
- Runtime logic doing the entitlement check and the multilanguage API support
- Management API-enabling programmatic update of the entitlement data in the persistent store
The persistent datastore is a fancy name for a relational database that's needed to save the entitlement information. It doesn't really matter which kind of RDBMS, since the runtime performance doesn't depend on it (it's all in the memory).
Here are some pointers that can really make interaction with the persistent datastore easier:
Using a database that supports Java triggers can be helpful. We can use those triggers to spawn an update to all the replicas of the user privileges table residing in multiple entitlement engines' JVMs. This is where the Observer design pattern comes in handy for a simple pub/sub interaction between the persistent datastore to multiple entitlement engines (see Figure 9).
Our object model is not complicated, but mapping it to a relational database isn't trivial. Manually coding the persistence layer is labor-intensive and error-prone. This is where a good object-relational mapping tool is useful. Any object-relational mapping tool eliminating hand-coded JDBC calls will do.
Our object model contains an OID for every class (OID stands for Object Identifier). An OID is a persistent immutable object identifier. The OID representation on the database is a column in each table. The OID representation in a programming language is a member variable with the following characteristics:
Note: The Interface Police pulled me over several times before for not writing enough abstraction layers in the form of stored procedures. They won't rest before forcing the entire developer community to write thousand of lines in redundant code - adding little or no value from a business perspective. In my opinion there is exactly one place where a database stored procedure might be useful for an entitlement engine. That place is figuring out the content of the user privileges table (covered later). For all the rest, dynamic SQL will do just fine.
- It shouldn't have any business meaning.
- It should be a numeric data type.
- It should be immutable; once allocated it never changes.
- It should be allocated automatically via a mechanism that guarantees uniqueness.
Owners of applications protected by the entitlement engine literally expect entitlement checks to be performed in no time. The performance requirements are divided into three distinct steps:
1. Our persistent datastore will always keep an up-to-date precompiled mapping between the users and their privileges.
2. The user-to-privilege mapping is cached in the entitlement engine JVM (java.util.Map.interface). The cache key is the user and the cache value is the list of privileges affecting this user.
3. The cached user to privileges mapping is kept current via a pub/sub mechanism.
The in-memory mapping between users and their privileges practically guarantees a high runtime performance since there isn't much to be done during the entitlement check. We narrowed it down into two simple steps:
1. A single Map lookup returns the collection of all privileges granted to a user.
2. A comparison of the user privileges with the requested action at hand boils down to a single pass of a loop.
From a performance standpoint, this is as good as it gets. Here is an explanation of the overhead omitted by using the precompiled user privileges list.
A user can obtain privileges from three sources:
The precompiled list of user-privileges mapping allows us to bypass the lengthy process of figuring out all the privileges owned by a user.
- By a privilege granted directly to the user
- By a privilege granted to a group the user belongs to
- By a privilege granted to a group containing a group the user belongs to
Keeping Replicas Current
The important thing to keep in mind is that the in-memory, user-privileges list must be synchronized with the persistent precompiled table of user privileges for all the instances of the entitlement engine. It's likely that an enterprise will simultaneously run multiple entitlement engines per one persistent datastore (as described in Figure 9).
The synchronization interval should be configurable via a property/configuration text file.
The triggering of the synchronization process should originate from the persistent store. It happens after a management API modification of the entitlement information. A database trigger might be useful for spawning this process.
In an ideal world the in-memory user privileges' list would be kept current without any disruption to runtime performance. One way to get close to this ideal is by keeping two instances of the user-privileges HashMap cache when only one of the two is active. Doing so enables us to update one copy in the background. (This is somewhat similar to the Copy-on-Write design pattern introduced in Concurrent Programming in Java, by Doug Lea.) This approach sacrifices some memory in the form of an additional Map instance.
To Use XML or Not?
The requirements list explicitly mandates support for multiple languages. There are several roads we can take for multilanguage support:
Another key requirement mentioned in the requirements list is performance. I found that building an XML-DOM tree for each request is significantly slower in comparison to a Java or stream protocol.
- Using XML
- Using byte stream protocol
- Writing wrappers to the Java API to various languages (C, C++, Perl, etc.)
Possibly XML will become a valid option due to an increase in processing power, improvements in XML parser technology, or usage of the SAX protocol. For the purposes of this example, however, I don't believe XML is an option for today's high-performance entitlement engines.
A Byte Stream Protocol Alternative
An alternative approach is defining a byte stream protocol using predefined delimiters. A StringTokenizer can construct the ProtectedItem, users/groups, and actions based on the delimiter location within the incoming byte stream. The power of this approach comes from delivering a single API supporting multiple languages with a high performance.
Notice that by using a byte steam protocol more work is delegated to the user community of the entitlement engine.
We could write a wrapper to the Java API to each and every language used within our enterprise , but it inflates our code base.
Privilege Check Algorithm
Before diving in, let's recap privileges. A sample privilege can be verbalized in the following manner: allow user John Smith to perform the action view on the ProtectedItem com.abcbank
Notice several important points:
The default policy is to return false from a privilege check. Unless a privilege permitting an action is explicitly specified, access is denied.
Privileges are applied in a downward fashion throughout the ProtectedItem tree. If a privilege is applied to the ProtectedItemcom.abcbank.investment, then it also applies to the ProtectedItem com.abcbank.investment.research.
After a privilege is applied to some ProtectedItem, the only way to block it from the hierarchy is by another privilege - denying the action in the original privilege.
Our inbound goods are the following:
This is the method body:
- User privileges list
- Requested ProtectedItem
- Requested action
Create a variable holding the most relevant privilege.
Loop all the privileges in the user-privileges list.
Check if the privilege action matches the requested action or its denial.
Check if the privilege ProtectedItem is located above or at the location of the requested ProtectedItem.
If both checks above are positive, the privilege is relevant. Compare it with the variable holding the most relevant privilege. The one closer to the requested ProtectedItem in the ProtectedItem tree takes precedence. The privilege with precedence is now in the temporary variable holding the most relevant privilege.
Exit the loop.
Evaluate the value of the variable holding the most relevant privilege. If this privilege allows access, return true; for anything else, return false.
Two people contributed a vast amount of information about this topic: Victor Podvalny and Danny Padwa from Goldman Sachs. Victor and Danny pioneered the technology of enterprise entitlement engines. The entitlement engines they designed and coded deliver a world-class performance and functionality.
- Ambler, S.W. (2001). Mapping Objects to Relational Databases.
- Brown, W., Malveau, R.C., McCormick, H.W., and Mowbray, T.J. (1998). AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. Wiley.
- Lea, D. (1999). Concurrent Programming in Java. Addison-Wesley.
Giora Katz-Lichtenstein is an independent software consultant.
specializing in Object Relational Mapping, OO A&D,middleware,
application servers, and the economy of software projects.
He is currently constructing a commercial high performance
enterprise entitlement engine.