Hi Amy,

On Jul 19, 2011, at 5:55 AM, Amy Worrall wrote:

> Hi! I have a question about good app design.
> 
> I know that, as mentioned in the WOWODC videos I've watched, a common
> beginner mistake is to put most of the logic in the page components.
> Indeed, I've been guilty of that myself in apps I've made in the past.
> I know the fundamentals of MVC from my background in Cocoa.
> 
> So suppose I have an app where users have their own profile. Each user
> can edit his own profile, whereas an admin can edit any profile.
> 
> Where abouts does the logic go to check if someone is authorised to
> edit a particular profile?


You're right. This is definitely not view code.  There is no one correct answer 
to this question. Every approach has tradeoffs.  The main question you have to 
ask yourself is this: Is this model or control logic?  

If it is model logic, it never changes with that particular model.  An admin is 
always able to edit any profile. If you reuse this user framework in another 
app, the same rule always applies.  If that is the case, then your answer is 
model logic.  

If however there are grey areas, then it becomes control logic. Consider a 
Photo entity.  In a photo sharing application, maybe everyone can read Photos.  
However, in a medical imaging app, only certain people are allowed to read 
certain photos due to privacy regulations. 

In that case, the authorization logic is control code.  Ideally, you would not 
want to put access control code on your Photo entity, because that code needs 
to change based on the application where the framework will be used.  If you 
put the logic in the model, then you will end up rewriting your photo model 
every time you need photos in a new app.


> Should there be a method on the Session, to
> return a boolean for "can edit this profile"? If that's the case, from
> where is that method called?


If you do it that way, you'll be tying your view components to a particular 
implementation in your session.  So, while it may work, it may not be the best 
approach.  I would suggest creating a reusable java interface in your view 
framework if you do it this way.  If you want to use these views in another 
application, you will know you have to implement the session interface you 
produced.


> I know I could do it by having the page component call the
> authorisation method, and return an error page instead if it goes
> wrong. But that seems to tie the logic too much to my view: what if I
> come to add a REST API later? I'd need to duplicate my permissions
> logic, since it wouldn't be using the WOComponent that outputs the
> HTML page. Ditto if I add another page elsewhere that happens to be
> able to make a profile change (say, allowing an inline name change on
> another otherwise unrelated page). Ideally I think the data model
> itself should be able to reject an edit if it's performed without
> permission, but then we get into problems since the data model
> shouldn't know about the session.
> 
> Also, I'm considering using Direct To Web (at least to some extent)
> for this project. I've never used it for anything more than an admin
> interface (i.e. one global login, if you're in then you can edit
> everything). If I were using Direct To Web, is the answer to the above
> question the same?

The approach Direct To Web takes is to provide a delegate object on the page 
component to handle control code.  Page components that need to trigger control 
code will call nextPage() on the delegate to return the next page.  Obviously, 
you can see how it can become confusing if every button on a page calls 
nextPage(), but expects different results.

ERDirectToWeb extends this with branching delegates.  Instead of having a 
single nextPage() method, a branching delegate has multiple methods.  So the 
delegate may have an editUser(), inspectUser(), deleteUser() method.  This 
makes it much easier to produce different results for different interface 
elements.  Whether or not these control methods are available in the interface 
is determined by the branchChoices RHSKey in the rule system. So, continuing 
with the example, you could have a couple of rules like

100: *true* => branchChoices = (inspectUser)
105: session.objectStore.user.isAdmin => branchChoices = (inspectUser, 
editUser, deleteUser)

Now, if the user in the session's objectStore is an admin, then inspect, edit, 
and delete will be available in the interface. Otherwise, only inspect is 
available.  

Personally, I find this approach the simplest.  The main problem I run into 
here is rule system caching.  ERD2W does a fantastic job of caching to keep the 
rule system really fast, but in the case of auth logic, it isn't hard to 
imagine ways that cached values can become invalid. 

My approach then, was to put together an auth framework to extend this a bit 
further and remove the rule system caching from the equation.  The framework is 
available in one of my github repositories.

https://github.com/nullterminated/ponder

ERAuth manages authentication and authorization.  It manages authorization for 
CRUD logic with the CRUDAuthorization class.  This class provides generic 
methods like 

        public Boolean canReadProperty(EOEnterpriseObject eo, String keyPath) {
                return Boolean.FALSE;
        }

Using a special ERASelector class, these methods can be overridden by methods 
with different signatures like:

        public Boolean canReadProperty(User eo, String keypath) {
                if(User.USERNAME_KEY.equals(keypath)) { return true; }
                if(isActor(eo)) {
                        return Boolean.TRUE;
                } else {
                        return Boolean.FALSE;
                }
        }

So that if the method is called for the class User, then the special User 
method is used. Otherwise, the ERASelector falls back to the User's parent 
entity until it reaches the base EOEnterpriseObject method.  This allows me to 
provide special logic for specific entities without a huge if block like:

if("User".equals(entity.name())) ...
else if
else if
else if
baaaarrrrffff

This approach also provide the ability to set auth controls all the way down to 
specific attributes. Using a method like this, it isn't hard to imagine a list 
view where table cells have a 'lock' icon only on certain rows and columns, 
depending on a user's auth privileges. Your doctor can read your photo's, but 
he can't read your neighbors... and all of the logic is encapsulated in a 
single auth delegate class.

The primary downside to ERAuth is that it requires support built into the view 
components. Right now, only one 'look' framework can use it... R2D2W. It's 
pretty new code as well, so don't be surprised if you find a bug or three (^_^)

Ramsey


> 
> Thanks for your help,
> 
> Amy
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Webobjects-dev mailing list      ([email protected])
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/webobjects-dev/ramseygurley%40gmail.com
> 
> This email sent to [email protected]

Attachment: smime.p7s
Description: S/MIME cryptographic signature

 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list      ([email protected])
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to