Purpose
Allow for flexible configuration of authorization logic within the framework. The authorization checks should be well exposed (i.e. visible). It should be straight forward to add new checks, or modify existing ones. It should be possible to use GaP as the authorization engine, alternative engines and their combination.
Implementation
The implementation uses ACEGI declarative authorization proxy factories, providing a voter class implementation (PermissionVoter
) that talks to GaP back-end. PermissionVoter
is configured with a permission target resolver (a bean whose task is to identify permission target from either configuration attributes or the arguments of the intercepted method call).
Example
Here's an example of how ACEGI constructs can be used together with a PermissionVoter
to protect a mock portlet definition registry:
<!-- the registry being protected --> <bean id="mockPortletDefinitionRegistry" class="org.jasig.portal.security.acegi.MockPortletDefinitionRegistry"/> <!-- the security interceptor that in this case is configured to protect portlet definition regisrty calls --> <bean id="portletDefinitionSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager"><ref local="authentication"/></property> <property name="accessDecisionManager"><ref local="portletDefinitionAccessDecisionManager"/></property> <property name="objectDefinitionSource"> <!-- Below is a mapping of intercepted methods to configuration attributes that will be passed to the voter In this case "remove*" and "create*" methods are configured with the activity attribute and the target (portlet definition id) is picked up by the resolver from the method call arguments. The update* method is configured with both activity and target (prefix for targets is PORT_ID in this case, see target resolver configuration below) - in this case target resolver will pick up the target from the config attribute instead of looking at the arguments passed to the method. --> <value> org.jasig.portal.portlet.registry.general.IPortletDefinitionRegistry.update*=ACTIVITY_UPDATE,PORT_ID.* org.jasig.portal.portlet.registry.general.IPortletDefinitionRegistry.remove*=ACTIVITY_DELETE org.jasig.portal.portlet.registry.general.IPortletDefinitionRegistry.create*=ACTIVITY_CREATE </value> </property> </bean> <!-- decision manager for portlet definition access --> <bean id="portletDefinitionAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> <property name="allowIfAllAbstainDecisions"> <value>false</value> </property> <property name="decisionVoters"> <list> <!-- GAP voter --> <bean class="org.jasig.portal.security.acegi.PermissionVoter"> <property name="targetResolver"> <ref local="portletDefinitionTargetResolver"/> </property> <property name="permissionOwner" value="UP_FRAMEWORK"/> <property name="authorizationService"> <ref local="authorizationService"/> </property> <property name="userController"> <ref local="userController"/> </property> <property name="activityPrefix" value="ACTIVITY_"/> </bean> </list> </property> </bean> <!-- permission target resolver for portlet definition id targets --> <bean id="portletDefinitionTargetResolver" class="org.jasig.portal.security.acegi.PortletDefinitionIdResolver"> <property name="targetPrefix" value="PORT_ID."/> <property name="allowMissingTarget" value="true"/> </bean> <!-- proxy configuration: this can be used to protect multiple beans, but in this case only mock registry is proxied --> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <value>mockPortletDefinitionRegistry</value> </property> <property name="interceptorNames"> <list> <value>portletDefinitionSecurityInterceptor</value> </list> </property> <property name="proxyTargetClass" value="true"/> </bean>
Suggested use pattern
The PermissionVoter
is configuredwith the permissionOwner
property, and a helper bean that can resolve permission targets (targetResolver
). The example above suggests that a given access decision manager should be associated with a single owner and a single target type. This should make configuration more transparent and more efficient. For instance, a different decision manager, portletDeploymentDecisionManager
, would be created to check authorization to publish portlets.
Mixed configurations
The described approach can be very flexible considering that additional permission voters can be configured for the same decision manager. For instance, if the installation maintains alternative set of permission information (let's say a role-based permission information that's entirely separate from GaP), then a second voter, RoleVoter
could be configured in parallel with the existing PermissionVoter
(this would require PermissionVoter
to abstain in situations when it doesn't have enough information to make a decision - right now it defaults to denying access).