Implementing read only access control in WebSphere Commerce

Follow through a sample implementation of read-only access control in WebSphere® Commerce. The scenario builds on allowing different users different levels of authority (read and write), depending on the role they play in the Buyer Organization, and restricting who can create orders and who can only view orders.

Mike Callaghan, Software Developer, IBM

Author1 photoMike Callaghan is a Software Developer at the IBM Toronto Lab, Canada. He has been part of the WebSphere Commerce Support team since 2005, specializing in runtime components. He was also part of the DB2 Development Infrastructure team, specializing in UNIX scripting and automation. He graduated with Honors from McMaster University, Canada, with a Bachelor's degree in Software Engineering


developerWorks Contributing author
        level

Kevin Kam (kevinkam@ca.ibm.com), Software Developer, EMC

Photo of Kevin KamKevin Kam is an Advisory Software Developer at the IBM Toronto Lab, Canada. He is the security and session management development lead in WebSphere Commerce.



Wesley Philip (wphilip@ca.ibm.com), Software Developer, IBM  

Photo of Wesley PhilipWesley Philip is a Software Developer at the IBM Toronto Lab, Canada. He is the development owner of the Member subsystem of WebSphere Commerce.



26 August 2009

Introduction

In WebSphere Commerce, the Access Control framework secures resources by granting authority to a set of users and executing a set of commands on a particular set of resources. Users are generally granted authority to the resource if they play a certain role in the organization, which owns the resource, or if they have a special relationship with the resource. There are cases, however, where the out-of-the-box policies do not cover all the specific security requirements. The policies may be too lenient, too restrictive, or do not provide the necessary distinction between creating new objects and reading existing ones. For example, a common scenario is Buyers creating and displaying orders for their Buyer Organization in a B2B environment. The OrderReadCommands restrict users to only display an order if they are the creator of the order.

This article will explain a scenario where read-only access control is to be implemented. Buyers in the organization will have full authority to create and view orders for the organization for which they are a Buyer. However, for any organization for which they are only a participant, they can only read or display the orders. For a list of out-of-the-box roles and what they are intended for, refer to the WebSphere Commerce Information Center topic, Organizational management roles.


Sample scenario and requirements

There are many different scenarios where some forms of read-only privileges are required for certain types of users.

The scenario explored in this article will be based on a B2B store model, where the users will play the Buyer role in one Buyer Organization (Buyer Org A), and an Organization Participant in another Buyer Organization (Buyer Org B). There is one store in this scenario that is owned by the Seller Organization. Both Buyer Org A and Buyer Org B are entitled to place orders for their respective organizations within Store A. The user has a Registered Customer role in Seller Org A.

Figure 1. Organizational hierarchy and role assignment for sample scenario
Organizational hierarchy and role assignment for sample scenario

Requirements

The requirements for the scenario are:

  • The user can log into Store A (B2B Store in Figure 1).
  • The user can select either Buyer Organization A (their parent organization) or Buyer Organization B (as a participant) as their active organization from the Selected Organization drop-down in the store front. Refer to the WebSphere Commerce Information Center for details on enabling this option.
  • If the user is a Buyer for the active organization, upon logon, he can create new orders, or see any existing orders placed by that organization.
  • If the user is only a Participant for the active organization, upon logon, he can view any existing orders placed by that organization, but not create new orders.

To accomplish the requirements, there are a few steps involved:

  1. Modify the Order Create and Order Read commands to return the Organization Resource in getResources().
  2. Add an access control policy to only allow Buyers to create orders for the organization where they have the Buyer (buy-side) role.
  3. Add an access control policy to allow buyers or participants to read all orders for the organization.
  4. Add an access control policy to allow Organization Participants to display the order beans for others in the organization.

Modify the Order Create and Order Read commands to return Organization Resource in getResources()

Currently, the Order Create and Order Read commands return only the store entity object as a resource within their getResources() method. This means that access control checks will be done only against that object, checking whether the current user can place an order within the store. One requirement is that the user is a Registered Customer in the organization who owns the store (or one of its ancestors). This will not work in our scenario though, as we are dealing with the same user who is always a Registered Customer within the store. We want to restrict the creation of orders based on the Buyer Organization for which the order is to be placed.

We need to have this additional restriction, but the Order Create and Order Read commands only return the StoreEntity object, so you need to extend the getResources() method of these commands to return an additional resource: the buying organization. You will need to extend the commands, which will be used in your storefront.

As an example, looking at the out-of-the-box defaultAccessControlPolicies.xml, you can see the following common OrderCreate commands:

   <!-- Action Groups specific to Order subsystem -->
   <ActionGroup Name="OrderCreateCommands"
            OwnerID="RootOrganization">
      <!-- Order-create activity commands -->
      <ActionGroupAction Name="com.ibm.commerce.orderitems.commands.OrderItemAddCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.orderitems.commands.OrderItemUpdateCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderCopyCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderScheduleCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.orderitems.commands.OrderItemMoveCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.orderitems.commands.
       AdminOrderItemUpdateCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.requisitionlist.commands.
       RequisitionListSubmitCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.orderquotation.commands.
       OrderQuotationCreateCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.marketing.commands.
       AddOrderItemWithPromotionCodeOrCouponCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderCreateCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.
       AddPredefinedConfigurationToOrderCmd"/>
   </ActionGroup>

Similarly, you can also find the OrderRead commands here:

<ActionGroup Name="OrderReadCommands"
            OwnerID="RootOrganization">
      <!-- Order-read (view) activity commands -->
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderDisplayCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderCopyCmd-Read"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderListCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.OrderScheduleCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.requisitionlist.commands.
       RequisitionListCopyCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.SetPendingOrderCmd"/>
      <ActionGroupAction Name="com.ibm.commerce.order.commands.SetOrderTemplateCmd"/>

....
    </ActionGroup>

In the extended commands, you will not only need to return the StoreEntity resource, but also a resource for the current active organization in the session.

Sample code that does this follows:

EntitlementContext entCtx = (EntitlementContext) 
	commandContext.getContext(EntitlementContext.CONTEXT_NAME); 
Long activeOrgId = entCtx.getActiveOrganizationId(); 
OrganizationAccessBean orgBean = new OrganizationAccessBean(); 
orgBean.setInitKey_MemberId(activeOrgId.toString()); 
orgBean.refreshCopyHelper(); 
iResourcelist.addElement(orgBean);

Note this is not a complete implementation, just an example of the code that can determine the active organization and add it to the resource list. You will still need to call the super.getResources() method at the beginning of the overwritten method to have the superclass add the StoreEntity resource.

For more details about implementing getResources(), refer to the WebSphere Commerce Information Center.

Depending on your particular requirements, you may need to also do something similar for the OrderPrepare commands. Refer to the OrderPrepareCommands ActionGroup within the defaultAccessControlPolicies.xml for the list of commands.


Add a new policy to restrict buyers to create orders

Once the Order Create and Order Read commands return the Buying Organization object as a resource, you can create new access control policies to restrict access to the creation of orders based on this additional resource. The out-of-the-box access control policy for creating orders is:

<!-- Create an order: 
  AllUsers  
  execute OrderCreate commands  
  using Store resources --> 
<Policy Name="AllUsersExecuteOrderCreateCommandsOnStoreResource"  
   OwnerID="RootOrganization"  
   UserGroup="AllUsers"  
   ActionGroupName="OrderCreateCommands"  
   ResourceGroupName="StoreEntityDataResourceGroup" 
   PolicyType="groupableTemplate"> 
   </Policy>

This policy will grant access for the OrderCreate commands shown earlier in the StoreEntity resource. However, since you have now added the buyer organization as an additional resource to be checked, you need to add a new access control policy to cover that case.

Based on the scenario described earlier, you need to set the UserGroup of the policy to Buyers(buy-side), as you are restricting the order creation to only Buyers (not Organization Participants).

Below is an example of how to create the user group Buyers(buy-side)ForOrg. Inclusion in this User Group has the condition of the user playing the Buyer (buy-side) role in the organization resource, or in one of its ancestor organizations:

<UserGroup Name="Buyers(buy-side)ForOrg" OwnerID="RootOrganization" 
Description="Buyers and participants"> 
<UserCondition><![CDATA[  
  <profile>  
    <simpleCondition>  
     <variable name="role"/>  
     <operator name="="/>  
     <value data="Buyer (buy-side)"/>  
     <qualifier name="org" data="OrgAndAncestorOrgs"/>  
    </simpleCondition> 
</profile> 
]]></UserCondition> 
</UserGroup>

For the policy, you need to set the Resource group to the out-of-the-box group that contains the Organization resource:

<!-- Create an order: 
  Must be a Buyer in the organization --> 
<Policy Name="BuyersExecuteOrderCreateCommandsOnOrganizationResource"  
  OwnerID="RootOrganization" 
  UserGroup="Buyers(buy-side)ForOrg"  
  ActionGroupName="OrderCreateCommands"  
  ResourceGroupName="OrganizationDataResourceGroup"  
  PolicyType="groupableTemplate"> 
</Policy>

Add new policy to allow buyers or participants to read orders for the organization

The next requirement is to allow either a Buyer or a Participant to read (or display) any other order created within the organization for which they play the Buyer or Participant role. The order creation was restricted to just Buyers, but the order reading is allowed for both roles.

Similar to the OrderCreate policies, the policy below shows the out-of-the-box for Order Read commands:

<!-- Display an order: 
  AllUsers  
  execute OrderRead commands  
  using Order resources  
  if the user is the Creator  
  We are making this a template policy to  
  allow future Store/Org based customization--> 
<Policy Name="AllUsersExecuteOrderReadCommandsOnOrderResource"  
   OwnerID="RootOrganization"  
   UserGroup="AllUsers" 
   ActionGroupName="OrderReadCommands" 
   ResourceGroupName="OrderDataResourceGroup"  
   RelationName="creator"  
   PolicyType="groupableTemplate"> 
</Policy>

This policy grants access for all users to execute the Order Read commands against an order, as long as they are the creator of the order resource (as per the RelationName specified).

To achieve the read-only criteria, you need to create another policy to handle the additional Organization resource returned by the Order Read commands extended earlier. Unlike the example above, where a user needs to be the creator of the Order resource, the new policy will grant access to either the Buyer or Participant to see the orders owned by the organization. Again, you first need to create a new User Group to identify the Buyers and Organization Participants for a specific Organization:

<UserGroup Name="BuyersAndParticipantsForOrg" OwnerID="RootOrganization" 
Description="Buyers and participants" > 
<UserCondition><![CDATA[  
 <profile>  
  <orListCondition>  
   <simpleCondition>  
    <variable name="role"/>  
    <operator name="="/>               
    <value data="Buyer (buy-side)"/> 
    <qualifier name="org" data="OrgAndAncestorOrgs"/> 
   </simpleCondition> 
   <simpleCondition> 
    <variable name="role"/> 
    <operator name="="/> 
    <value data="Organization Participant"/> 
    <qualifier name="org" data="OrgAndAncestorOrgs"/> 
   </simpleCondition> 
  </orListCondition> 
</profile> 
]]></UserCondition> 
</UserGroup>

You will use this as the UserGroup in the new policy, as opposed to AllUsers from the out-of-the-box policy. Note that we also have removed the relation restricting the resource to be created by the user, since you want to view all orders created by the organization instead of just those created by the user himself.

<Policy Name=
"BuyersAndParticipantsForOrgExecuteOrderReadCommandsOnOrganizationResource" 
 OwnerID="RootOrganization"  
 UserGroup="BuyersAndParticipantsForOrg"  
 ActionGroupName="OrderReadCommands" 
 ResourceGroupName="OrganizationDataResourceGroup" 
 PolicyType="groupableTemplate"> 
</Policy>

Add new policy to allow participants to display the order beans for others in the organization

The first two new policies you have created dealt with the Order Create and Order Read commands. You need to also grant Participant authority to the Display the Order databean resources.

Out-of-the-box, you have the policy below, which restricts authority to only the creator of the Order databean to display it, again based on the RelationName specified.

<!-- AllUsers 
 can display OrderDatabeanResourceGroup  
 if the user is the creator --> 
<Policy Name="AllUsersDisplayOrderDatabeanResourceGroup"  
 OwnerID="RootOrganization"  UserGroup="AllUsers"  
 ActionGroupName="DisplayDatabeanActionGroup"  
 ResourceGroupName="OrderDatabeanResourceGroup"   
 RelationName="creator"   
 PolicyType="groupableStandard"> 
 </Policy>

Here you will add a third policy, to not only allow Particpants to display their own orders, but any order placed by the same Buyer Organization.

<Policy Name="ParticipantsOfOrgDisplayOrderDatabeanResourceGroup" 
 OwnerID="RootOrganization"  
 UserGroup="AllUsers"  
 ActionGroupName="DisplayDatabeanActionGroup"  
 ResourceGroupName="OrderDatabeanResourceGroup"  
 RelationGroupName="ParticipantOf->BuyerOrganizationalEntity"  
 PolicyType="groupableStandard"> 
 </Policy>

You use the existing RelationGroup ParticipantOf->BuyerOrganizationalEntity to add this stipulation.

This relation checks for all the organizations for which this user plays the organization participant role. For each of these organizations, it will check if the user has a BuyingOrganizationalEntity relationship with the resource (the order).


Add new policy to allow buyers to display the order beans for others in the organization

Similar to the previous policy, you need to allow Buyers to display the order databeans for any other orders placed by the same Buyer Organization. Here you need to make a new relation group to capture this criteria:

<RelationGroup Name="Buyers(buy-side)->BuyerOrganizationalEntity" 
OwnerID="RootOrganization">
 <RelationCondition><![CDATA[  
  <profile>  
   <openCondition name="RELATIONSHIP_CHAIN">    
    <parameter name="ROLE" value="Buyers(buy-side)"/>  
    <parameter name="RELATIONSHIP" 
      value="BuyingOrganizationalEntity"/>  
   </openCondition>  
  </profile>  
 ]]></RelationCondition> 
 </RelationGroup>

We will use this relation group in the final new access control policy:

<Policy Name="BuyersOfOrgDisplayOrderDatabeanResourceGroup" 
 OwnerID="RootOrganization"  
 UserGroup="AllUsers" 
 ActionGroupName="DisplayDatabeanActionGroup"  
 ResourceGroupName="OrderDatabeanResourceGroup"  
 RelationGroupName="Buyers(buy-side)->BuyerOrganizationalEntity"  
 PolicyType="groupableStandard"> 
</Policy>

Subscribe organizations to the new Access Control policies

You have created four new access control policies to grant authority in the scenario. During runtime, only those policies that are tied to an Access Control Policy Group being subscribed to by the Organization, who owns the target resource, will be evaluated. You need to add each of these four policies into a policy group.

You will add the first two policies to the B2BPolicyGroup. This policy group is subscribed by the sell-side organization, which is needed here since the resources are orders owned by the sell-side.

<PolicyGroup Name="B2BPolicyGroup" OwnerID="RootOrganization"> 
 <PolicyGroupPolicy 
  Name="ParticipantsOfOrgDisplayOrderDatabeanResourceGroup" 
  PolicyOwnerID="RootOrganization" /> 
 <PolicyGroupPolicy Name=
  "BuyersOfOrgDisplayOrderDatabeanResourceGroup" 
   PolicyOwnerID="RootOrganization" /> 
   </PolicyGroup>

For the other two policies, the resources returned by the policies are the buy-side organizations. Here you will make a custom policy group with these two policies. You will also subscribe to this policy group by the parent of all Buyer Organizations who are using these policies.

<PolicyGroup Name="CustomPolicyGroup" OwnerID="RootOrganization"> 
 <PolicyGroupPolicy Name="BuyersExecuteOrderCreateCommandsOnOrganizationResource" 
PolicyOwnerID="RootOrganization" />    
<PolicyGroupPolicy   
 Name="BuyersAndParticipantsExecuteOrderReadCommandsOnOrganizationResource" 
 PolicyOwnerID="RootOrganization" /> 
  <!-- Define organizations which subcribe to this policy group -->
  <PolicyGroupSubscription OrganizationID="<the parent of your buyer 
  organizations>"/>
</PolicyGroup>

Since this parent Buyer Organization subscribes to a policy group, the policy groups subscribed by its parents will no longer apply. The following will subscribe the parent Buyer Organization to the ManagementAndAdministrationPolicyGroup for access to the generic administration actions:

<PolicyGroup Name="ManagementAndAdministrationPolicyGroup" 
 OwnerID="RootOrganization">
	<PolicyGroupSubscription OrganizationID="<the parent of 
    your buyer organizations>"/> 
</PolicyGroup>

These policies can all be loaded using the acpload utility. The new UserGroups is loaded using the acugload utilty. Links to these utilities are provided in the Resources section.


Conclusion

This article showed one example of granting full access or read-only access based on specific roles and organizations, rather than relationships, to the resource. In this case, you granted full access for buyers to create orders for their buyer organization, and read or displayed any other order for that organization. For organization participants, you restricted to read-only access on the orders. This is just one case; there are many possible scenarios that can be done based on business and security requirements.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=422740
ArticleTitle=Implementing read only access control in WebSphere Commerce
publish-date=08262009