I am starting a web application with client side implemented in pure ExtJS and middle tier in Grails. The application has role-based authorization, where a user can have many fine grained roles like SOME_FORM_READ, SOME_FORM_UPDATE, SOME_DATA_DELETE, SOME_DATA_READ, etc. Based on the roles of the user, certain GUI elements need to be disabled or hidden, while others need to be in a read-only mode.
I did some search on the web, but didn't find any design pattern that specifically addresses this issue, so I came up with my own design. I am sure that a lot of the web applications out there will have a similar requirement, so I'd like to post my design here and hear people's opinion on it. By no means is my design a perfect one, but I hope it can be improved with everyone's input. Although I am working with ExtJS, the general design should also apply to similar frameworks like GWT, Flex, Swing, etc.
There are four types of code (or information) we need to deal with in the client tier regarding authorization:
GUI element manipulation code, for example:
panel.hide() form.setReadOnly(true)
GUI element permission requirement, for example:
form.requires('READ', 'FORM_READ_ROLE')
adminPanel.requires('ADMIN_ROLE')
User privilege information, which is basically a list of roles that the user has;
Authorization logic: determines which elements to hide/disable based on user privilege;
The core of the design is a singleton, named GUIPermissionManager, or GPM for short. This is a centralized design in that most of the code is in GPM, so that GUI elements are not polluted by the authorization code. This is how GPM works:
GUI elements (that need certain permission to access) register their permission information with GPM, like this:
GPM.register(this, 'DEPARTMENT_DELETE_ROLE'); // button for deleting a department
GPM maintains a list of GUI permission registration
On user login, GPM receives the list of roles the use is assigned
GPM walks through the GUI permission registration list and based on user privilege, determines which part of the GUI to hide, and in turn, calls element.hide() accordingly
Questions:
Q1: Hierarchical UI element hiding - Optimizing your GPM to avoid hiding elements that are already hidden via a parent is not going to have much of a performance boost in my opinion. My reasons:
If you really want to keep track of hierarchical information you can always use the 'contains' method that all container components provide for checking if a DisplayObject is contained anywhere in its child list (including down the chain). This could be called up each time a component is registered to check if it already has a registered parent.
A flag could then be set in a dictionary to ignore hiding on that component. This flag could get checked first while iterating over the list of registered components to determine what should be hidden. The dictionary could use keys that correspond to the registered component's UID. Furthermore this flag could be used to ignore the component when it comes time to ignore other GPM functions, like form disabling (as the form would never be seen anyways).
Q2. Off the top of my head you could disable/enable components, implement state changes, or intercept events, and all alerts. This is really too broad of a question as anything could be done - really up to the designer.
Q3. You could:
You would essentially be establishing a contract with various components where the GPM is aware of their interfaces and interacts with them accordingly.
Q4. You can always set a form to be disabled (enabled = false). This prevents any user interaction. Some skins will change to indicate that the components are disabled so you may want to modify their skins to prevent some of this display behavior. On that line, you could also change their skins to hide certain elements such as the TextInput box's border so as to make it look more like a 'view' than a disabled input.
It would be possible to create a 'transformer' that changes TextInputs with RichText components and such. This would take a decent amount of work and should probably be built into an extended Form class instead of the GPM. I think different skins states for each component type may be a better solution, so as to avoid creation and destruction of components just to change how the form appears.
A note of caution! User priviledges/authorization should be controlled on the server side and not on the client side especially for js based webapps. Its a big security risk. You should consider that in your framework.
edit - what is your framework for client side auth/priv management? That will mostly direct your client side GUI management.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With