In the previous post I detailed the roller coaster ride of implementing e commerce enablement to the consumer site that I'll be launching in a few weeks. The service plan options that I provide allow users to manage their own private conference room in the basic "free" configuration, additional plans that require payments allow a user to manage or create multiple rooms. The problem I ran into revolved around how to provide the users the ability to create new rooms in a limited fashion. Originally I thought that the uniqueness of the problem constrained the generality of the solution so that all I needed to do was upgrade the User class to add a new "create room token" which was simply an integer indicating the number of available requests to create a room that the associated user could invoke. This solution however broke the symmetry of the permissions system in that it granted a right that the permissions granted outside of the ken of the permissions system structure. I was able to implement and test the room tokens in a day of coding but something about the solutions asymmetry just bugged me.
Two days ago, after I'd finished testing the e-commerce integration with Amazon I decided to come up with a more generalized solution to the problem. I would add into the existing framework API a mechanism for creating permission invocation limits that was completely agnostic of the type of action being permitted. To do this I realized the optimal solution would be to map the permission_id associated with all valid permissions in the system with a user_id and finally tie it together with a count or invocation limit which specifies the number of executions remaining for the corresponding permission by the associated user. This solution would require the creation of a new Permissions_token table , each row indicating a token unique to the user and permission. The User objects of my framework already are mapped in a one to many method with permissions, the new table would allow another one to many relationship to exist between the User and permission_tokens.
Create the Permission_Token class...
The next step was to create a new class to manage the Permission_Token rows to be added and modified in the database. The Class simply contains three parameters and their mutator methods and a method to output an xml representation of the object. This would be used to manipulate ordered sets of the tokens for extraction from the db or insertion to it, it also makes managing the rows in the User class facile.
Update the class methods...
After creating the new Permissions_Tokens table and the PermissionToken class I had to update the User class to provide the methods that allow mutation of the permission tokens for a User. I'd need a collection object to store the set of Tokens, for that I used a private ArrayList object. Then I provided methods for adding, removing , getting , and updating PermissionToken items from the the array list. These methods performed various tests on the Tokens depending on their action, for example the "add" methods tested for the existence of a Token by ensuring the permission_id field doesn't already exist in the ArrayList. This adds additional computational expense but makes it impossible to add the same Token to the array. Also, the remove and update methods required unique implementation to ensure synchronized mutation of the array under concurrent modifications. After adding all the methods and testing in main() I was ready to move to the next most important step. Implicit implementation of the permission_token methods into the User class.
One of the greatest advantages of object oriented design is the ability to use encapsulation to hide underlying changes to a class implementation that could significantly change the inner workings of a class. The proper selection of change points inside the methods of a class allows class programmers to make deep foundational changes to code without affecting any of the client code that uses those classes. In my framework the User class is the nexus through which the security framework provided by the permissions system is expressed. Users are given permissions and client code queries the Users for their ability to perform actions associated with the client code. For example, to access an administrative interface a User must be able to view the interface, which requires a "view" permission for system objects. The client code in the administrative interface queries all User requests for this "view" permission, those that have it are allowed access to the resource those that don't are denied. This key based system is preferred over group based permissions (like used in windows) precisely because of the granularity it provides. However, once a permission is granted to a User the user (barring Flag restrictions to their account access) has unlimited rights to invoke it, thus any user with "view" rights to the administrative interface can view that interface as long as they are logged and not Flag limited.
The permission_tokens allows us to apply invocation limits on the execution of any permission but to prevent its inclusion from causing changes to client code the method must be implemented in the User class methods that are interrogated to determine if a User has a given permission. The "hasPermission" variants. There are several types, the first type simply iterates over the collection of Permission objects for a User until a requested target permission type or type id is encountered, if it is found the User has the permission if not they do not. The calling code then performs an action based on the response. The next type are "hasImplicitPermission" methods.
A little background on the Permissions API is required here, the Permissions all map to a Permissions db table with an int valued primary key, the Permissions have a "entity_type" , "right_id" and an "entity_type_id" field , the former indicates the fully qualified class name of the class type associated with the permission, the latter indicates the id in the db table for that class type. All entity classes have a corresponding table and corresponding permissions generated when the entities are added to the system. If we specify a positive value for the "entity_type_id" we then have a permission for a particular instance of the given type for the indicated "right_id". The "right_id" maps to a table that simply lists the actions that can be performed on any instance of any type.
View
Create
Update
Delete
Search
Import
Export
Publish
Different types perform different actions for each right based on what it is made for, but a glaring problem presents itself. If each instance is tied to a permission, how do we provide the permission to perform a given "right" over ALL instances of a given type if we don't know yet what all the instance Id's are???? We need a permission that grants global application of a right over all instances, the obvious solution is to use a zero valued "entity_type_id", this indicates that all instances of the given type for the given right as a permission. With a global permission for every right of every type we now have an extremely granular permissions granting system. Some users will be given permissions only to perform rights on specific instances of a type and no other, other Users who may have management duties can be given the general zero permission for a right allowing them to perform it for all instances "implicitly". Thus comes the relevance of the "hasImplicitPermission" method types of the User class.
The hasImplicitPermission methods perform a test to determine if a User has permission over a given instance even if they don't have that instances permission. Thus Users with the global permission for a queried entity type will return "true" from this method when any instance id is indicated as the entity type id. Using implicit permissions a User can be granted access to a large set of instances without actually having the instance permission, the general permissions exist also for a special class of system managed rights. For example, the ability to update configuration setttings on a node (a server running a copy of the framework software) the User must have the Config "Update" permission, which is a global permission type. Also, since all Entities derive from a base class , it is possible to grant "view" permissions across all entities of any type by granting the permission for the base class...thus with as little as 16 permission objects a User can be granted "God" powers over the entire framework, from creating and modifying configuration settings across nodes, to creating and publishing stories or thread posts. Collections of permissions known as permission sets can be created to grant desired permissions to Users expeditiously. All that said, the introduction of permission_tokens adds yet another dimension of granularity, allowing any permissions that a User does have to have different invocation limits for each one.
So to prevent the necessity of client code changes the best integration method involves modifying the "has...Permission" methods to internally factor in the existence of Permission_token items corresponding to the Permissions that a User currently possesses. If the User is being interrogated for the right to perform a specific right, the permission that matches that right would normally return "true" for the result of the method invocation but if the User has a Permission_Token for that permission, the count of the token must be determined as positive non zero, before the "true" is returned otherwise "false" is returned. This way the existing client code (which as of this late date extends into two applications built on the framework since the last major change to the permissions system over 2 years ago) will be untouched. This is the benefit of a deep analysis of the best place to include the required functionality, you make fewer changes but those changes are more powerful. Designs that predominate in changes of this sort tend to be the most efficient ones in my experience. In any event, including the test for the count of permission_tokens for an invoked permission takes advantage of the short circuit behavior of Boolean operations such that if a User doesn't have a permission requested the first argument of the check will fail out of the test and return "false" without having to iterate through the permission_tokens at all. Also, since only limited permissions have tokens , the default behavior is unlimited invocations for a Permission, this reduces the number of tokens that would need to be loaded with a User ensuring that the cost for iterating the set is always efficient for the ArrayList collection being used. The execution of code on demand ensures that memory utilization under concurrent actions by multiple users on the system ramps slowly rather than in a spiky fashion.
I am currently working on the db handler for the permission_tokens which allows mutated tokens to be retrieved or persisted back to the permissions_tokens table. Implementation to replace the previous "create room token" will follow...
Two days ago, after I'd finished testing the e-commerce integration with Amazon I decided to come up with a more generalized solution to the problem. I would add into the existing framework API a mechanism for creating permission invocation limits that was completely agnostic of the type of action being permitted. To do this I realized the optimal solution would be to map the permission_id associated with all valid permissions in the system with a user_id and finally tie it together with a count or invocation limit which specifies the number of executions remaining for the corresponding permission by the associated user. This solution would require the creation of a new Permissions_token table , each row indicating a token unique to the user and permission. The User objects of my framework already are mapped in a one to many method with permissions, the new table would allow another one to many relationship to exist between the User and permission_tokens.
Create the Permission_Token class...
The next step was to create a new class to manage the Permission_Token rows to be added and modified in the database. The Class simply contains three parameters and their mutator methods and a method to output an xml representation of the object. This would be used to manipulate ordered sets of the tokens for extraction from the db or insertion to it, it also makes managing the rows in the User class facile.
Update the class methods...
After creating the new Permissions_Tokens table and the PermissionToken class I had to update the User class to provide the methods that allow mutation of the permission tokens for a User. I'd need a collection object to store the set of Tokens, for that I used a private ArrayList object. Then I provided methods for adding, removing , getting , and updating PermissionToken items from the the array list. These methods performed various tests on the Tokens depending on their action, for example the "add" methods tested for the existence of a Token by ensuring the permission_id field doesn't already exist in the ArrayList. This adds additional computational expense but makes it impossible to add the same Token to the array. Also, the remove and update methods required unique implementation to ensure synchronized mutation of the array under concurrent modifications. After adding all the methods and testing in main() I was ready to move to the next most important step. Implicit implementation of the permission_token methods into the User class.
One of the greatest advantages of object oriented design is the ability to use encapsulation to hide underlying changes to a class implementation that could significantly change the inner workings of a class. The proper selection of change points inside the methods of a class allows class programmers to make deep foundational changes to code without affecting any of the client code that uses those classes. In my framework the User class is the nexus through which the security framework provided by the permissions system is expressed. Users are given permissions and client code queries the Users for their ability to perform actions associated with the client code. For example, to access an administrative interface a User must be able to view the interface, which requires a "view" permission for system objects. The client code in the administrative interface queries all User requests for this "view" permission, those that have it are allowed access to the resource those that don't are denied. This key based system is preferred over group based permissions (like used in windows) precisely because of the granularity it provides. However, once a permission is granted to a User the user (barring Flag restrictions to their account access) has unlimited rights to invoke it, thus any user with "view" rights to the administrative interface can view that interface as long as they are logged and not Flag limited.
The permission_tokens allows us to apply invocation limits on the execution of any permission but to prevent its inclusion from causing changes to client code the method must be implemented in the User class methods that are interrogated to determine if a User has a given permission. The "hasPermission" variants. There are several types, the first type simply iterates over the collection of Permission objects for a User until a requested target permission type or type id is encountered, if it is found the User has the permission if not they do not. The calling code then performs an action based on the response. The next type are "hasImplicitPermission" methods.
A little background on the Permissions API is required here, the Permissions all map to a Permissions db table with an int valued primary key, the Permissions have a "entity_type" , "right_id" and an "entity_type_id" field , the former indicates the fully qualified class name of the class type associated with the permission, the latter indicates the id in the db table for that class type. All entity classes have a corresponding table and corresponding permissions generated when the entities are added to the system. If we specify a positive value for the "entity_type_id" we then have a permission for a particular instance of the given type for the indicated "right_id". The "right_id" maps to a table that simply lists the actions that can be performed on any instance of any type.
View
Create
Update
Delete
Search
Import
Export
Publish
Different types perform different actions for each right based on what it is made for, but a glaring problem presents itself. If each instance is tied to a permission, how do we provide the permission to perform a given "right" over ALL instances of a given type if we don't know yet what all the instance Id's are???? We need a permission that grants global application of a right over all instances, the obvious solution is to use a zero valued "entity_type_id", this indicates that all instances of the given type for the given right as a permission. With a global permission for every right of every type we now have an extremely granular permissions granting system. Some users will be given permissions only to perform rights on specific instances of a type and no other, other Users who may have management duties can be given the general zero permission for a right allowing them to perform it for all instances "implicitly". Thus comes the relevance of the "hasImplicitPermission" method types of the User class.
The hasImplicitPermission methods perform a test to determine if a User has permission over a given instance even if they don't have that instances permission. Thus Users with the global permission for a queried entity type will return "true" from this method when any instance id is indicated as the entity type id. Using implicit permissions a User can be granted access to a large set of instances without actually having the instance permission, the general permissions exist also for a special class of system managed rights. For example, the ability to update configuration setttings on a node (a server running a copy of the framework software) the User must have the Config "Update" permission, which is a global permission type. Also, since all Entities derive from a base class , it is possible to grant "view" permissions across all entities of any type by granting the permission for the base class...thus with as little as 16 permission objects a User can be granted "God" powers over the entire framework, from creating and modifying configuration settings across nodes, to creating and publishing stories or thread posts. Collections of permissions known as permission sets can be created to grant desired permissions to Users expeditiously. All that said, the introduction of permission_tokens adds yet another dimension of granularity, allowing any permissions that a User does have to have different invocation limits for each one.
So to prevent the necessity of client code changes the best integration method involves modifying the "has...Permission" methods to internally factor in the existence of Permission_token items corresponding to the Permissions that a User currently possesses. If the User is being interrogated for the right to perform a specific right, the permission that matches that right would normally return "true" for the result of the method invocation but if the User has a Permission_Token for that permission, the count of the token must be determined as positive non zero, before the "true" is returned otherwise "false" is returned. This way the existing client code (which as of this late date extends into two applications built on the framework since the last major change to the permissions system over 2 years ago) will be untouched. This is the benefit of a deep analysis of the best place to include the required functionality, you make fewer changes but those changes are more powerful. Designs that predominate in changes of this sort tend to be the most efficient ones in my experience. In any event, including the test for the count of permission_tokens for an invoked permission takes advantage of the short circuit behavior of Boolean operations such that if a User doesn't have a permission requested the first argument of the check will fail out of the test and return "false" without having to iterate through the permission_tokens at all. Also, since only limited permissions have tokens , the default behavior is unlimited invocations for a Permission, this reduces the number of tokens that would need to be loaded with a User ensuring that the cost for iterating the set is always efficient for the ArrayList collection being used. The execution of code on demand ensures that memory utilization under concurrent actions by multiple users on the system ramps slowly rather than in a spiky fashion.
I am currently working on the db handler for the permission_tokens which allows mutated tokens to be retrieved or persisted back to the permissions_tokens table. Implementation to replace the previous "create room token" will follow...
Comments