On Feb 27, 2012, at 8:47 PM, Adrian Crum wrote:

> I would welcome a discussion of wrong (or bad) patterns. Lately I spend about 
> half my development time fixing things that are done wrong.
> 
> -Adrian

Thank you Adrian, here is what I would like to address: I see a lot of code 
that checks ROLE permissions in a way that make them useless or redundant.
Before I describe the issue that I see I would like to summarize what I know 
about ROLE based permissions, it would be useful to check if we are all on the 
same page with these as well.

There are two main families of permissions (SecurityPermission) in OFBiz and 
they are categorized based on a naming convention:

a) standard permissions are in the format <NAME>_<ACTION>; for example: 
ACCOUNTING_VIEW, CATALOG_UPDATE, ...; a user with one these permission can 
perform the ACTION on all data related to <NAME> domain; for example a user 
with CATALOG_UPDATE can update any catalog in the system

b) role permissions are in the format <NAME>_ROLE_<ACTION>; for example: 
ACCOUNTING_ROLE_VIEW, CATALOG_ROLE_UPDATE, ...; a user with one these 
permission can perform the ACTION on the data related to <NAME> domain but only 
if the party (associated to the user) is associated to (has a role) the data; 
the nature of the "association" depends on the domain/context; for example a 
user with CATALOG_ROLE_UPDATE can update only the catalogs in the system for 
which the user is in the role of LTD_ADMIN (this information is stored in 
ProductCategoryRole); however the "association" could be more complex than a 
role record; for example we could have a special ROLE permission that grants 
some action on some employee data to users that are managers in the company 
division where the employees work: the "association" here would be a 
combination of PartyRole/PartyRelationship (several records, possibly on a 
hierarchical tree); but the general rule is: if the user has a ROLE permission 
then additional checks on data are needed before granting rights to perform the 
operation on data and they depend on the context and business rules

The bad pattern I see in the system is exemplified by the following code 
snippet from setPaymentStatus service:

        <check-permission permission="ACCOUNTING" action="_UPDATE">
            <alt-permission permission="ACCOUNTING_ROLE" action="_UPDATE"/>
            <fail-property resource="AccountingUiLabels" 
property="AccountingPermissionError"/>
        </check-permission>

A check like this returns true if the user has ACCOUNTING_UPDATE or 
ACCOUNTING_ROLE_UPDATE; in this way the two permissions are equivalent.

For the same reason, the following permission services are not very useful:

    <simple-method method-name="basePermissionCheck" 
short-description="Accounting component base permission logic">
        <set field="primaryPermission" value="ACCOUNTING"/>
        <call-simple-method method-name="genericBasePermissionCheck" 
xml-resource="component://common/script/org/ofbiz/common/permission/CommonPermissionServices.xml"/>
    </simple-method>

    <simple-method method-name="basePlusRolePermissionCheck" 
short-description="Accounting component base permission logic">
        <set field="primaryPermission" value="ACCOUNTING"/>
        <set field="altPermission" value="ACCOUNTING_ROLE"/>
        <call-simple-method method-name="genericBasePermissionCheck" 
xml-resource="component://common/script/org/ofbiz/common/permission/CommonPermissionServices.xml"/>
    </simple-method>

in fact:

* basePermissionCheck returns true if the user has one of the base ACCOUNTING 
CRUD+ADMIN permissions
* basePlusRolePermissionCheck returns true if user has one of the base 
ACCOUNTING CRUD+ADMIN permissions OR if user has one of the base 
ACCOUNTING_ROLE CRUD+ADMIN permissions

How should the two services be used properly? If we run only 
basePlusRolePermissionCheck we will not know if we have to check if the 
party/user is "associated" to the data because we do not know if the user has 
an ACCOUNTING permission or only an ACCOUNTING_ROLE permission.
In theory the two services should be used together in the following way:
1) run basePermissionCheck and if it returns true then grant right to perform 
the action;
2) otherwise, run basePlusRolePermissionCheck and if it returns true then check 
"association" data (but this,even inside of the ACCOUNTING domain, may depend 
on different data structures for different processes); if the data is available 
then grant right to perform the action

At this point it would be more useful to have the following base services:

* basePermissionCheck: same as above
* rolePermissionCheck (instead of basePlusRolePermissionCheck): returns true if 
user has one of the base ACCOUNTING_ROLE CRUD+ADMIN permissions

As a side note, for the same reason the methods security.hasRolePermission(...) 
are useless if you do not pass the roles (and no code does currently) and were 
all used in the wrong way.

The end result is that we have a lot of code that treats standard permissions 
and ROLE permission as equivalent; this happens mostly for two reasons:
A) bad implementation; for example see service orderAdjustmentPermissionCheck)
B) incomplete implementation (the code to check the "association" is still 
missing); for example see service acctgAgreementPermissionCheck

In my opinion we should fix #A and #B by returning "false" if the user has a 
ROLE permission only but the code doesn't check for "association" data and we 
will add a placeholder TODO comment as a reminder: if the code is not 
implemented then the ROLE permission should not work rather than (as it happens 
now) working as the standard permission

I apologize for the long email, I look forward at your feedback.

Jacopo




Reply via email to