4.2 Creating a Permission Hierarchy4.2.1 ProblemYou need to create a system for maintaining a simple permission hierarchy for a trading system. This hierarchy needs to be similar to the one described earlier in Section 4.1.1. Permissions should be manageable in groups so a set of permissions can be granted to a trader easily. In addition, your solution needs to allow for permission types to be added dynamically while the system is in production. 4.2.2 SolutionDefine two tables. In the first table, define groups of permissions; in the second table, link those groups to particular traders. Each trader can belong to more than one permission group. The table defining the permission groups can be defined as follows: CREATE TABLE GroupPermissions( GroupId VARCHAR(20) NOT NULL, ProductType VARCHAR(10) NOT NULL, Status CHAR(1) CHECK(status in ('V','S')) DEFAULT('V'), Limit NUMERIC(10,2) NULL, PRIMARY KEY(GroupId,ProductType) ) In our GroupPermissions table, there are two attributes describing trading permissions for each product group. One attribute is a status attribute. The status of each trading permission can be set to valid ('V') or suspended ('S'). The second attribute is an order limit that defines the maximum permitted size for a trade involving the product group. If a trader is authorized to trade bonds and has a trading limit of 1,000, that trader is not allowed to initiate a trade involving more than 1,000 bonds. The following is a representative sample of the type of data that you could store in this table: GroupId ProductType Status Limit -------------------- ----------- ------ ------------ Debt Bill V 10000.00 Debt Bond V 10000.00 Derivatives Future V 200.00 Derivatives Option V 100.00 Equities Share V 1000.00 The second table needs to keep track of the permission groups assigned to each trader. Its definition can be as follows: CREATE TABLE GroupMembership( AccountId VARCHAR(20) NOT NULL, GroupId VARCHAR(20) NOT NULL PRIMARY KEY(AccountId,GroupId) ) This table simply links accounts with groups. Whenever a trader is assigned to a new group or removed from an existing group, the table must be updated to reflect that fact. The following sample shows the group assignments for three traders: AccountId GroupId -------------------- -------------------- Alex0001 Debt Alex0001 Derivatives Alex0001 Equities Betty0002 Derivatives Charles0003 Debt You can see from this example that a single trader can belong to multiple groups. In this case, Alex0001 belongs to three groups: Debt, Derivatives, and Equities. In the next section, we'll describe one way to handle cases where a trader's permissions from one group conflict with his permissions from another group. 4.2.3 DiscussionThe main goal of our solution is to separate the definition of permissions and groups from the assignment of groups to traders. Please note that the GroupPermissions table is not fully normalized. To normalize it, you would have to separate the definition of groups from the definition of products and then use an intersection table to define the Limit and Status attributes. The lack of normalization here simplifies the example somewhat, but it doesn't affect the overall behavior of our design nor does it affect the queries that you'll see in this section.
4.2.3.1 Checking permissions for a traderQueries regarding trading authorizations can easily be performed on the model that we've designed. For example, to list the trading permissions for the account Alex0001, all you need is the following simple join: SELECT m.AccountId, g.ProductType, MIN(g.Limit) Limit FROM GroupMembership m JOIN GroupPermissions g ON m.groupId=g.groupId WHERE Status='V' AND AccountId='Alex0001' GROUP BY m.AccountId, g.ProductType AccountId ProductType Limit -------------------- ----------- ------------ Alex0001 Bill 10000.00 Alex0001 Bond 10000.00 Alex0001 Future 200.00 Alex0001 Option 100.00 Alex0001 Share 1000.00 While the results of this query are straightforward, the use of the GROUP BY clause together with the aggregate function MIN deserves some additional explanation. The grouping allows us to deal with cases where more than one of a trader's permission groups defines a limit for the same product. In our example data, both the Debt and Equities groups define a trading limit for bonds. Alex0001 is a member of both groups, so an ungrouped query would return the following two records: AccountId ProductType Limit -------------------- ----------- ------------ Alex0001 Bond 10000.00 Alex0001 Bond 2000.00 With two limits for the same product, the question is which limit takes precedence. In our example query, we used the MIN function for the lower limit to take precedence over the higher limit. If you wanted the higher limit to take precedence, you would make that happen simply by using MAX instead of MIN. The GROUP BY clause is a requirement when using aggregate functions in this manner and ensures that only one permission is ultimately returned for each product that the trader is authorized to trade. 4.2.3.2 Revoking permissions for a groupThe Status column in our design allows you to quickly and efficiently revoke a specific permission from a group or to suspend all permissions for a given group. For example, to suspend all trading permissions for the Debt group, you could execute the following statement: UPDATE GroupPermissions SET Status='S' WHERE GroupId='Debt' The previously shown query for checking permissions only looks at valid permissions (Status = 'V'); those you've just suspended (Status = 'S') will automatically be excluded. Their definitions, however, will still exist in the database, so you can easily enable them again when you want to do so. The solution shown in this recipe is a general solution and can be used for almost any type of permission hierarchy with a limited number of levels. It can be used for security hierarchies where different levels of security authorization are defined, for storing information regarding types of users and their authorizations in a web-based authorization system, or for any other type of permission hierarchy where a large number of users or accounts is used. |