4

Suppose you have entities A, B, C and D.

  • D relates to C
  • C relates to B
  • B relates to A

Furthermore, user is only allowed to operate on D, if user owns A.

At a certain state in an application, you include a link to a page, which accesses D. Thus, you includes D's ID as a GET or POST parameter.

If user clicks on the link, the application retrieves D's ID and begins to operate on D.

Simple apps use URLs like this [modulo URL-rewriting]:

http://www.myServer.com/?action=1234&entity=D&ID=23

How to verify if user is allowed to operate on D?

A) The obvious solution would be this: Given D, find C, then find B and eventually find A. If the chain breaks somewhere, access to D would be rejected. Unfortunately, this requires - if trivially implemented - 4 database accesses instead of just the one for A.

B) Another solution would be to keep D's ID in the current session in a set of accessible entities to be used by the next page to be rendered.

C) As an alternative, one could encrypt GET and POST parameters somehow. On each page request, the first operation would be to decrypt the request's parameters. If the decrypt operation fails, access would be denied.

D) Or, at infinitum, hash all links in all pages, keep a map in the session which associates the hashes with the URLs and write only hashes to webpages.

E) Finally, you could keep references to A, B and C in D, references to A and B in C, references to A in B. Thus, at each level, one would be able to find immediately the root entity.

What's your solution in such a situation? And why?

Although I included the PHP tag, I'm don't want to focus this question to a language. I'd be happy to get general proposals. Or solutions, which are already implemented in e.g. ORM layers.

UPDATE-1

Finally, I have chosen D).

General principle:

Ensure, that IDs of somehow subordinate entities always get passed in a secure/trusted way. In such a way, that a third party isn't able to change their values.

Details:

This option provides many benefits by design:

First, IDs or other parameters of the linked pages never reach the browser. Instead of

http://www.myServer.com/?action=1234&entity=D&ID=23

most pages gets linked like this

http://www.myServer.com/?forwardHash=78sd7sdf98asd7ad5aa76asa4a465

All parameters of the next page to be executed are fully kept inside the user's session.

Since all parameters of pages are kept inside the user's session, far less checking is needed. Especially, the above mentioned relational dependency checks aren't any longer of use. If something is in the user's session, it has been put from a previously trusted dialog step.

Moreover, one can even force the user to only call those link available on the currently rendered page. Each time they call a link, the app may invalidate all other links of the page. Thus users won't be able to open pages in several windows and think, that they see two different 'state' of the application. If they call a link twice, the app may present an error message.

Finally, one may directly establish something I'd call sub-workflow dialogs: You would start the dialog by pushing the current page's URL on a continuation stack in the session and opening the edit dialog step. User may either orderly end or intentionally cancel the dialog workflow. A cancel workflow link may automagically appear as a user option, if the continuation stack isn't empty.

By keeping the continuation on a stack in the session, it gets fully isolated from the currently running dialog step. The dialog step not even knows anything about its caller.

By wrapping the functionality inside a small manager calls, the sub-process finally calls FlowManager::finishFlow(). This call pops a continuation from the stack and redirects the browser to this page - effectively returning to the point, where the workflow started.

Since we use a stack of continuations, one could even run sub-workflows which are subordinate to other sub-workflows.

SteAp
  • 11,853
  • 10
  • 53
  • 88
  • 4
    Am I right in saying that A/B/C/D is essentially hierarchical data, and you could look through [Bill Karwin's options for hierarchival data](http://www.slideshare.net/billkarwin/models-for-hierarchical-data), finding one that would enable you to get the entire tree (=rights) in one query? – Wrikken Jul 21 '11 at 22:37
  • Yes. One could see the situation as hierarchical structured data. I'll have a look. – SteAp Jul 21 '11 at 22:39
  • Is it true to say that there could be any number of links in the chain between the entity being edited and the entity owned by the user - it's not limited to the three links in your example? – Mike Jul 21 '11 at 22:52
  • It's fixed but may vary: A to C, or A to D. I'm not in search of the reflexive hull of a potentially unlimited link chain. – SteAp Jul 21 '11 at 22:59
  • Does the user have to own each entity in the chain, or just the final entity in the chain? Also, if an entity is removed from the chain, should the chain break, or remain valid? For example, given `D > C > B > A`, and user owns `A`, does the user still own `A` if `B` is removed - i.e., does the chain become `D > C > A`? – Mike Jul 22 '11 at 20:11

2 Answers2

2

The obvious solution would be this: Given D, find C, then find B and eventually find A. If the chain breaks somewhere, access to D would be rejected. Unfortunately, this requires - if trivially implemented - 4 database accesses instead of just the one for A.

I suppose that might be possible. It depends in part on what "relates to" means, but assuming a relatively straightforward schema, I'd expect you to be able to join all four tables in a single SQL statement. If part of the chain is missing, the query would return no rows.

Or am I missing something?

Mike Sherrill 'Cat Recall'
  • 91,602
  • 17
  • 122
  • 185
  • No, nothing. But beside verifying that D belongs somehow to the user, I don't want to force D's controllers know too much about their environment. Ideally, D's controllers shouldn't know much about how D relates to other entities. – SteAp Aug 03 '11 at 21:55
  • If you confine client access to a stored procedure, D's controllers won't know anything about relations between entities. (Assuming "D's controllers" refers to client code.) They'll only know what data you return. (And possibly column names, although you can alias those in the SP.) – Mike Sherrill 'Cat Recall' Aug 03 '11 at 22:17
1

I'm not sure I understand what you want to achieve but can't you use option A, to verify if user is allowed to operate on D with only one access to the database?:

SELECT D.*
  FROM D
    JOIN C 
      ON C.id = D.cid
    JOIN B
      ON B.id = C.bid
    JOIN A.id = B.aid
WHERE A.ownedBy = @userID
  AND D.id = @idToBechecked  
ypercubeᵀᴹ
  • 113,259
  • 19
  • 174
  • 235
  • Quite true. Unfortunately, this approach requires D to know, that it depends on C, that it depends on B.... E.g. a 'Edit D' controller shouldn't know anything about other entities it depends on. Ideally, if I mode the 'Edit D' controller to another project, it should still be able to work unchanged - even if the entity type C no longer exists and the relation B:D is 1:n. In other words, I don't want to force D's controllers to be fully abstract and know anything about D's relations. – SteAp Jul 31 '11 at 20:49
  • @Stefan: In that case, you can probably use an hierarchical approach. See here for various options: http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database – ypercubeᵀᴹ Jul 31 '11 at 21:05
  • Thx! Yes, I'm aware of such structures. The question appeared during the implementation of an ORM, btw. – SteAp Jul 31 '11 at 22:51