Wednesday, 17 September 2014

WebSphere Commerce access policy not working for a command

Problem

You developed a new custom WebSphere Commerce command, you defined access policy for it and you are sure it is correct but for some reason, your command executes without confirming to your access policy.

What might be the root cause ?

Fix

Access policy are defined for interfaces and not for the actual command. So if your command is not picking up the access policy, then you didn't define the relation between your command and its interface properly. Example:

Let's assume, your new custom command is called ProcessRefundCmdImpl and its interface is ProcessRefundCmd.  Let's assume as well, you defined NAME & defaultCommandClassName properly in your interface.

If your ProcessRefundCmdImpl is defined as shown below, two things will happen:
  • The command will execute as you expect and implementation class ProcessRefundCmdImpl will be picked up from CMDREG or defaultCommandClassName (as you expect)
  • Once you apply access policy, command will not work anymore.
public class ProcessRefundCmdImpl extends ControllerCommandImpl
Now, once you change your code to be as shown below, your access policy will start working as expected.

public class ProcessRefundCmdImpl extends ControllerCommandImpl implements ProcessRefundCmd

It is one of those small mistakes that can take hours or even days to find out. It would have been much better, if the command never executes if it doesn't implement the proper interface in first place, but it is there for developers to have fun with.


Why would you need to define access policy WebSphere Commerce data beans ?

We -WebSphere Commerce developers- don't pay much attention to security data beans, we put all attention to WebSphere Commerce commands and views because they are supposed to be entry point to any store and they will enforce security, while data beans are only accessible from a JSP which is the last step in the cycle and you can't get it to it unless you go through a command or view first.

That was my belief till a network administrator send me the curl command below which he used to simulate some request parameters, send it over to one of our JSPs (CustomerSearchOutput.jsp) and without providing any credentials, he managed to extract customer sensitive data from the database.

curl -k --data "storeId=10151&qlist=1n2n3n4n5n6n7n8n9n10n11n12n11n12n&useraction=searchclicked&sortclicked=defaultsortclicked&searchOption1=findbylastname&searchOption2=&searchTerm1=Duncan&searchTerm2=&csrLogonId=Rumi&sorttype=desc" https://wcs_testserver/webapp/wcs/csr/servlet/CustomerSearchOutput.jsp ?

As you might imagine, we worked till late the next couple of day to get it sorted out. For better security, you need to make sure no one can access your databeans and use them as a gateway to extract data from your database.

For details on how to get it done, please check my earlier blog Implementing access control in WebSphere commerce data beans

Implementing access control for WebSphere commerce data beans


Introduction

Tried recently to secure some data beans, followed WebSphere Commerce infocenter and didn't manage to make it work ? banged your head against a brick wall for hours and still didn't work ? Welcome in club, you are not alone.

Assumptions

Let's assume we have the following details to start with:

  • Data bean to secure is com.mycompany.beans.SampleDataBean
  • The bean is declared as follows
public class SampleDataBean extends SmartDataBeanImpl
  • The bean is intended for administrators only and should be available for members of group CSRSystemAdmins

Steps

  • Change your bean to implement both Protectable & Delegator as shown below
public class SampleDataBean extends BaseSearchBean implements Protectable, Delegator
  • Implement getDelegate as shown below. This method is the most important for security to work and most properly, things didn't work for you because you didn't implement getDelegate and left it to just return null, which for Commerce (DataBeanManager to be more specific) means security will be ignored (even if you defined access policies). You need to return a reference to any object that implements Protectable and since my sample implements it, I will return a reference to it (which is this). 
@Override
public Protectable getDelegate() throws Exception {
String METHOD_NAME = "getDelegate()";
LOGGER.entering(CLASS_NAME, METHOD_NAME);
return this;
}
  • You can leave fulfills without a particular implementation as shown below. In the sample scenario explained here, I enabled tracing and am sure fulfills was never called during bean initialization which means it will not impact how am expecting bean to work. On other hand, if you are doing more changes and you will define a relation between policy and resource, then you need to have a proper implementation.
@Override
public boolean fulfills(Long member, String relationship) throws Exception, java.rmi.RemoteException {
String METHOD_NAME = "fulfills(Long member, String relationship)";
LOGGER.entering(CLASS_NAME, METHOD_NAME);
return false;
}
  • Implement getOwner as shown below. Again it doesn't make a big difference, I just decided that the owner is whoever executing it. You can change it to whatever you see more appropriate.
@Override public Long getOwner() throws Exception, java.rmi.RemoteException { String METHOD_NAME = "getOwner()"; LOGGER.entering(CLASS_NAME, METHOD_NAME); return getCommandContext().getUserId(); }
  • Now for access policies, you need to amend your access policies to have something as shown below. I marked in red, the pieces you need to focus on while adding the new access policy. Remember the order of the sections below is important.
<Action Name="DisplayDatabean" CommandName="Display"/>
<ActionGroup Name="DisplayDatabeanActionGroup" OwnerID="RootOrganization"><ActionGroupAction Name="DisplayDatabean"/></ActionGroup>   <ResourceCategory Name="SampleDataBean" ResourceBeanClass="com.mycompany.beans.SampleDataBean"><ResourceAction Name="DisplayDatabean"/></ResourceCategory> <ResourceGroup Name="DataBeansForAdmins" OwnerID="RootOrganization"><ResourceGroupResource Name="SampleDataBean"/></ResourceGroup> <Policy Name="DisplayDataBeansForAdmins"OwnerID="RootOrganization"UserGroup="CSRSystemAdmins"ActionGroupName="DisplayDatabeanActionGroup"ResourceGroupName="DataBeansForAdmins"PolicyType="groupableStandard"></Policy>
  • Load the new policy using acpload
  • Make sure you don't have any errors in acpload.log
  • Open Organization administration console and review the entries are properly loaded
  • Enable access control tracing as explained in an older blog Debugging WebSphere Commerce
  • Restart server
  • Start testing and access your JSPs as usual. You need to access the JSPs with a guest user, a logged-in user who is not a member of group CSRSystemAdmins
  • If security check is successful and user ganted access, you should something similar to the trace output below

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed isAllowed? User=344003; Action=Display; Resource=com.mycompany.beans.SampleDataBean; Owner=344003; Resource Ancestor Orgs=-2001; Resource Applicable Orgs=-2001

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed Found PolicyName: DisplayDataBeansForAdmin; PolicyType: 2; PolicyOwner: -2001

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Entry

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Policy Applicable Orgs=-2001

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Exit

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy Evaluating PolicyName: DisplayDataBeansForAdmin
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy CommandLevelCheck: false; StoreId: 10151
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy No Relationship or RelationshipGroup to check
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed PASSED? =true
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed Exit
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isAllowed PASSED? =true
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isAllowed Exit


  • If on other hand, user denied access, you will see something as shown below

com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed isAllowed? User=333003; Action=Display; Resource=com.mycompany.beans.SampleDataBean; Owner=333003; Resource Ancestor Orgs=-2000,-2001; Resource Applicable Orgs=-2000
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed Found PolicyName: DisplayDataBeansForAdmin; PolicyType: 2; PolicyOwner: -2001
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Entry
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Policy Applicable Orgs=-2000
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl getPolicyApplicableOrgs Exit
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy Evaluating PolicyName: DisplayDataBeansForAdmin
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy CommandLevelCheck: false; StoreId: 10151
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl evaluatePolicy Normal UserGroup does not match
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed PASSED? =false
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isExecutionAllowed Exit
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isAllowed PASSED? =false
com.ibm.commerce.accesscontrol.policymanager.PolicyManagerImpl isAllowed Exit



Final Recommendation

  • It is better you always test your security by enabling trace to make sure that a user is denied or granted access for the right expected reasons and according to your expectations for how the policy should work. Sometimes you can get the right results but for wrong reasons, for example, you have another policy (that you created for sake of testing long time ago) in your local environment that happens to do what you expect. In such cases, once your code is deployed to another environment, it will stop working and it will take you a long time to figure out why.

    References

    Tuesday, 16 September 2014

    Access policy looks fine but some entries are not loaded properly

    Problem

    You have created a custom access policy file e.g. CustomACPolicies.xml, you load it using acpload, you don't get any error but still when you check the entries in database or using Organization admin console, you realize some of the entries are missing and were not loaded properly.

    Fix

    Make sure to review the order of the elements in the file, because the order of different section makes a whole big difference, for example a ResourceCategory or ResourceGroup can't come after a policy. Here is the order you need to follow within the XML file:
    • Action
    • ActionGroup
    • ResourceCategory
    • ResourceGroup
    • Policy
    • PolicyGroup

    Example

    The following extract from an access policy when executes, it creates the policy as well as resource group as expected, but the resource group is empty and it doesn't contain the resource com.beans,ResultDataBean. 

    The reason is, the ResourceCategory should come before the ResourceGroup.

    <ResourceGroup Name="PenguinDataBeansForCustomerServiceSupervisors" OwnerID="RootOrganization">
    <ResourceGroupResource Name="com.beans.ResultDataBean"/>
    </ResourceGroup> 


    <ResourceCategory Name="com.beans.ResultDataBean" ResourceBeanClass="com.beans.ResultDataBean">
    <ResourceAction Name="DisplayDatabean"/>
    </ResourceCategory> 

    <Policy Name="DisplayPenguinDataBeanForCustomerServiceRepresentatives"
    OwnerID="RootOrganization"
    UserGroup="CustomerServiceRepresentatives"
    ActionGroupName="DisplayDatabeanActionGroup"
    ResourceGroupName="PenguinDataBeansForCustomerServiceRepresentatives"
    RelationName="owner"
    PolicyType="groupableStandard">
    </Policy>