Jim, I have a T4 application that uses domain-object level security implemented using the Acegi framework. I have used custom authorization code more appropriate for my task, rather than the built-in Acegi method.
It works beautifully, and I've certainly seen people attempting to access things they shouldn't. Jonathan > -----Original Message----- > From: Jim [mailto:[EMAIL PROTECTED] > Sent: Tuesday, April 22, 2008 10:08 PM > To: Tapestry users > Subject: Re: T5: Forms - Best Practice > > Hi Christoph, > > I know you're mostly asking about best-practice of process rather than > security, but I think it's nonetheless important to bring up the issues > that can arise when embedding primary keys of your entities client-side. > > I'm still a T4 user, but when you say "a PageLink on a PersonSearch > page, which has the Person's primary id as context", I assume that's > analogous to the "parameters" attribute on a DirectLink in T4, in that > the parameter is embedded in the page. Even if that's not the case, > I'll continue anyway :P > > Building on your example, let's say that the logged-in user should only > be able to search/edit people within his/her department. If we're only > passing the PersonID to the PersonEdit page, and that ID is embedded in > each rendered link on the PersonSearch page output, then the user could > hack the form/link from the PersonSearch page to pass an arbitrary > PersonID to PersonEdit, potentially giving the user the ability to > perform unauthorized edits. > > I've started taking the approach of embedding not the ID, but a piece of > information that's unique within the security-context (by > "security-context", in this case I mean "department"). In this case, > assuming that a person's full name is unique within a department, we > could embed the person's full name into the PageLink, and the PersonEdit > page would search not on the primary key of the person, but instead on > the combination of the DepartmentID (retrieved from the session if we're > keeping a User object in the session) and the full name we passed in. > Since we're using the DepartmentID from the session, then the user can > hack the form/link all he/she wants, and the best they'll do is bring up > the editing form for someone that wasn't in the search results but is > still in their department, so it'd still be an authorized action. > > This sort of approach is annoying, because we'd love to be cleanly using > solid efficient primary keys, but it probably pays to be paranoid. > Anyone have a better approach on this issue? Particularly with regard > to search pages? > > Jim > > > Christoph Jäger wrote: > > Hi, > > > > Sorry for this long post. I spent quite some time now to try to figure > > out how to use forms to edit/update objects the "right" way (you know, > > simple, stable, elegant, easy to read, easy add things, ...). I use > > Tapestry 5.0.11. I am almost satisfied with what I came up with now, > > but some improvements need to be done. Maybe someone on this list can > > help with some ideas. > > > > As an example, lets have a PersonEdit page, with a PersonDao injected. > > You can create new Person entries or edit existing ones. To edit an > > existing person, there is a PageLink on a PersonSearch page, which has > > the Person's primary id as context. To create a new Person, a PageLink > > with 0 as context is used. To make this work, we have onActivate() and > > onPassivate() methods in PersonEdit: > > > > void onActivate(int id) > > { > > _id = id; > > if (id == 0) > > { > > _person = new Person(); > > } > > else > > { > > _person=personDao.get(id); > > } > > } > > > > int onPassivate() > > { > > return _id; > > } > > > > This way we can avoid using @Persist on the Person property (because, > > for instance, we want a user to be able to open two browser windows, > > viewing two different Person entries side by side and edit and save > > both of them. I think this would be problematic if we use @Persist, > > but please correct me if I am wrong). > > > > Now, editing an existing user works like this: > > > > - click the "edit user XYZ" PageLink on the PersonSearch page > > - in onActivate(), the personDao is used to query the Person from the > > database > > - an HTML form is rendered to let the user edit the values > > > > Up to here everything looks perfect. > > > > - the user edits the Person's data and hits the "save" button > > > > - onActivate() is called, a fresh Person is loaded from the database > > - for each field in the HTML form, validation is done (if defined), > > and a property in the fresh Person instance is set if the validation > > was successful > > - onValidateForm() is called if existing to allow for cross-field > > validation > > - if all validations were successful, onSuccess() is called. Here we > > call _person=personDao.save(_person). This save method returns a new > > Person instance, exactly as it was written to the database (primary id > > may be generated by the database, time stamps or version numbers > > updated, ...). We use this new Person's id : _id=_person.getId() to > > make sure we have the correct if for the next onPassivate() > > - onPassivate() is called > > - result sent to browser, redirect > > - onActivate() loads Person again > > - new form is rendered > > > > This is good, but I think it could be improved. > > > > 1. The Person is loaded from the database twice (using > > personDao.get()), and saved once. The save() method of personDao > > already gives us a new, fresh instance of Person, it seems like a > > waste to load it again after the redirect. > > > > 2. During validation, we check if there is already a Person with the > > same userid (in onValidateForm(), or in onValidateFromUserId()) and > > warn the user if this is the case. But what happens if a new Person > > with this userId is added just after onValidateForm() is called, but > > before onSuccess() is called, where we want to save? To make our > > program solid, we have to take this into account. In case save() does > > not work, we do not want to see some exception page, but the form, as > > it was filled, with a hint what might have gone wrong, so the user can > > try again. To make this possible, we have to move the save() to > > onValidateForm(), because there we can still record form errors (I > > think there is a JIRA for an improvement to this situation). > > > > 3. We want to give the user feedback of what happened. After clicking > > the save button, we want to show a message like "Successfully saved > > new person information" above the form (at the same place you would > > see error messages like: "The first name is mandatory"). The only > > thing that comes to my mind is to use @Persist to save the message > > from the onSuccess() or onValidateForm() through the redirect to the > > next page render. But now I have problems to make sure this > > @Persist'ed message is cleared and is only presented to the user > > directly after the save button was clicked. > > > > > > I think to find a nice solution for these issues (you know, easy > > things should be easy to do, and a form like this does not sound very > > complex to me, so there should be an easy solution), a @Persist, which > > persists values just through the redirect would be very helpful. The > > problem is, I have no idea how this could work. But maybe an option > > like this already exists (I am sure I am not the first to have > > problems like this), and I just didn't find it. > > > > Any ideas are welcome, > > > > Thanks, > > > > Christoph Jäger > > > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: [EMAIL PROTECTED] > > For additional commands, e-mail: [EMAIL PROTECTED] > > > > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]