The other day I had to mess around with struts-menu. struts-menu is a neat little Struts plugin that draws drop-down Javascript menus on web pages. It's been quite useful thankyou very much, if a little sparsely documented.
I needed to make certain menu options disappear based on who was logged in. There's some example code for menu permissions provided with the struts-menu download. Matt Raible has a demo of it online. I couldn't find anything that vaguely resembled task-focused documentation, though.
Eventually, I worked out how it's done. Mostly by reading the source-code. Hence this write-up. I hope Google will make sure the next person with the same problem finds their way here, so they don't have to repeat the mild discomfort I had to go through to gather this information.
Goal
To limit access to particular menu items based on the application's security configuration.
Background: PermissionsAdapter
Checking the Javadoc, you'll find the PermissionsAdapter Interface. This interface has one method: "isAllowed(MenuComponent)" that is called for each menu and menu item when the menu is being drawn. If it returns true, the item is drawn, if it returns false, the item is not drawn. (Some menu implementations may draw, but disable disallowed menu items)
struts-menu comes with a single concrete implementation of the PermissionsAdapter, called RolesPermissionsAdapter. This adapter maps the user's security roles as defined in the application/appserver configuration against a list of permitted roles defined for each menu/menu item in menu-config.xml
Step 0: Define your roles
This is part of your general J2EE/JAAS/etc security configuration. Each user or group will be allocated roles. Those roles are mapped to role definitions in the application's configuration. It's a very good idea to sit down at a whiteboard with your use cases and map out what the various roles are and what they're allowed to do before implementing anything.
Step 1: Define a PermissionsAdapter for the menu
The JSP tag you use to display the menu, <menu:useMenuDisplayer/> has an optional property: "permissions". If this property is present, it's used as the key with which to look up an instance of PermissionsAdapter in the application/request/session/page scope. It will then apply that adapter to the menu.
There is, however, a "magic" value for this property, "rolesAdapter". This magic value tells the menu displayer that instead of looking for an adapter in scope, it should instead create a new instance of RolesPermissionsAdapter and use that instead.
So, step 1 is to go through all your invocations of <menu:useMenuDisplayer/> and add permissions="rolesAdapter" to the tag.
Step 2: Map roles to menu items in menu-config.xml
The <Menu/> and <Item> tags in your menu configuration file (which is variable, but I shall refer to as menu-config.xml) have an optional "roles" attribute. This attribute, when used in conjunction with the RolesPermissionsAdapter, is a space-separated list of all the roles that will be permitted to use a particular menu or menu-item.
RolesPermissionsAdapter works by splitting the list, and calling request.isUserInRole() for each one. If any of them come back true, the menu or menu item is displayed.
So, for the following menu item definitions:
<Menu name="PrefsMenu" title="Preferences" roles="User">
<Item name="UserPrefs" title="User Preferences" page="prefs.do"/>
<Item name="ModPrefs" title="Moderator Preferences"
page="modPrefs.do" roles="Moderator System"/>
<Item name="AdminPrefs" title="Site Preferences"
page="sitePrefs.do" roles="System"/>
</Menu>
The menu is visible to anyone in the User role, who then get access to User Preferences automatically. Anyone in the System or Moderator roles can access the moderator preferences, but the site preferences are limited only to System-level users.
Of course, this doesn't prevent people accessing the page in ways other than using the menu, but it's polite not to present people with options that you know they can't take advantage of.
Anyway, I hope this is of some use to someone other than me.
Nice write-up Charles - I'm working on documentation for the next 2.0 release of struts-menu today. I'll make sure and add this to the documentation (which I agree - sucks right now). BTW, in 2.0, the roles attribute accepts comma-delimited values instead of space delimited. It also uses Velocity for it's HTML/JavaScript rendering - giving developers complete control of the menu display logic.
Would it possible to get an example of menu-config.xml. I would like to
use struts menu but am relatively new to both.
Hi, Good job. Thanks for the info. I have 1 problem.
I want to put struts menu at a relative position, so that in 4th row of a table. To do that I used following, but it does not work in Netscape Naviagator, any body caan help me?
' >
Thanks,
Kam
This is great, it works really fine. I'd like to know, if there are menu implementations that draw the items disabled by roles grayed and not functional - I mean no js events and links...
Thanks
Hi. Firstly, as a Struts Menu wanna-be-user :-) I'd like to thank you for taking time to improve its documentation.
Secondly, after placing the question on SM SourceForge public forum and messing with code I still haven't been able to grasp the idea abput how to implement dynamic role checking to enable/disable menu options based on the security model defined for the application (role-based, declarative).
This is what I originally asked on the forum: "I'm wondering whether is possible to dinamically read user roles (i.e. from a database) instead of defining the comma-seppareted list in the corresponding cfg file."
This is what Matt Raible answered: "The easiest way (at this point) to do a dynamically read role thing is to your own RolesAdapter and do a database lookup or something."
The answer seems pretty logical, but how to implement it, well, that's another story... the devil's in the details... they say...
Best regards!
Hey Carlos,
I've been having the same problem. I'd really like to find a way to implement dynamic user roles instead of using the .cfg file.
Otherwise SM is an amazing addition to struts.
Whether it is possible to dynamically read user roles (i.e. from a database) instead of defining the comma-seppareted list in the corresponding menu.config files..I would be glad if somebody helps me out because i m stuck in struts menu..Plezz if u know the answer mail me on my email-id
Does the module cater for i18n? I have to display menus in multiple languages
Hi,great work! I have a question, what do you mean by;
"Some menu implementations may draw, but disable disallowed menu items"
I use 1.3,and it just disables,but I want it to be invisible.
Take care,
ok ok all.but inform lattest menus.if do u not inform ,u'r website waste.
Charles,
Thanks for your write-up. You are right, documentation on Struts Menu is thin, and your article here saved me a LOT of time. Using your info I was able to make my own simple implementation of PermissionsAdapter to determine menu item visibility using roles from the DB. Kudos.
Ken