cleanest way to restrict user to subset of data


I have a fairly large database where multiple organisations are stored. Users only need access to the data from one organisation so I need to make sure they can only access and change the data that pertains to them.

I have users and rights module installed which is working very well at restricting access to certain controllers/actions but im not sure of the best way to restrict the user to only their organisations records within a controller/action. The links within the pages will only ever let them see their own organisations data but the url could easily be overwritten with an id for data that does not belong to them.

In my data model, the organisation can be up to 6 levels up the hierarchy from the current data. I have all the required relationships set up so its pretty easy to write a line of code to go back up the tree to get the organisation id and compare it to the org id in their session profile (org id is set when user logs in) but Im just wondering about the overhead/efficiency in this.

For instance, I was thinking that in each model I could get the orgid in the OnAfterFind event, then check it against their profile org id then accept or reject them at this point.

So if the model is 6 levels down from the organisation table, what is the impact of running code like the following every time I get a record?

$orgid = $this->parent->parent->parent->parent->id;

I would only need to get to the table below the organisation table as it should have an orgid foreign key that I could use and therefore save that extra step.

As this is a security issue I need to implement the most robust option but with minimal coding, ie I dont what to have to add something to every controller/action, that will be painful. Im thinking that by attaching it to the model and having it fire when data is retrieved might be the most efficient way but have concerns with the overhead.

One way around this might be to load up all the ids from all the models that are valid and store them in the session, there might be 2,000 - 3,000 of these though so again, not sure of the impact/efficiency of this approach.

Any expert advice would be appreciated.


Greg J

Hi Greg

This is not a simple answer and I would also like to hear other coder’s opinions on this topic.

My users belongs to an enterprise, which consists of businesses, which consists of branches. All my tables are linked to either the enterprise, or one of the businesses or one of the branches.

The user logs in via his branch - which leads me to his business and enterprise.

I store the user’s enterprise-id, business-id and branch-id in session.

Then in defaultscope(), I check to which of these three id’s the table is linked and I then filter on them.

This means that defaultscope() permanently filters the records on the right enterprise, business or branch; and I only had to write it once to have it working for the whole application.

But, this is where the easy part stops, because I also give each record a security-level-code, which determines what records the user may create, read, update, delete. For example, the normal user-created records have a security-level-code that allows the user to CRUD them; but there are also system records which the user may only read.

To make matters even worse, I re-use controller actions and views as much as possible. So the same view will be used to display General Ledger transactions of both clients and for bank accounts. Some users may work with the client records while other users may only do the bank accounts. So again I need tests to determine what records to display and if the user has permission to work with them.

All this testing boils down to pretty much performing tests for each controller/action/view/scenario combination.

I am thus also open for suggestions to better ways of doing it.