Symfony Admin Generator in multi user setup (restricting records in LIST view) - symfony1

I am using SF 1.2.9 to build a website. I want to use the admin generator to provide admin functionality for the object models I have used (specifically LIST, edit and delete).
I have read the Symfony docs (Chapter 14), but unless, I am very much mistaken, all examples I have come accross so far, seems to be written for a single user environment only. Meaning that the list of records returned to the user is essentially, ALL the records in that table. In a multiuser environment, this is irresposible at best, and potentially, a security threat. It is a necessary requirement to restrict the list of records returned to a user to only those that they own (i.e. created).
Suppose I have a table with (YML) schema like this:
foobar_dongle:
id: ~
title: varchar(255)
info: longvarchar
owner_id: ~
created_at: ~
where owner id is a FK into a user table.
Assume I generate an admin module like this:
symfony propel:generate-admin backend FoobarDongle --module=dongle
Question:
How do I modify the list of records returned to a user in the LIST part of the code generated by the admin generator? As I mentioned above, currently, (i.e. out of the box), the admin generator presents the user (rather naively, I feel), with the ENTIRE set of records for the model being administered. I need to be able to restrict that list somehow, so that I can only return records owned by that user.
This is what I am trying to find out how to do.
I would be most grateful to anyone who can show me how I can restrict the list of records returned when using the admin generator for administration of an object model. Ideally, I would like to be able to specify a custom method that has all the custom 'filtering' logic - but so long as I can restrict the LIST of records a user can see (in admin), to only the records that he is the owner of, that is all I want to be able to do.

If you only want to restrict the returned objects in one or two modules, do this:
Go to the actions.class.php file of your module. There should be no methods by default and the class should inherit from autoModuleNameActions you. Insert the following method:
protected function buildQuery()
{
$query = parent::buildQuery();
// do what ever you like with the query like
$query->andWhere('user_id = ?', $this->getUser()->getId());
return $query;
}
But this becomes unhandy if you do it for more modules. In this case I would advice to create a new admin generator theme.
And if you want to make the query depending on some custom parameter in the admin generator config file, then you have to extend this file. But is not just done with adding a new parameter. You can read this article how to do this.
If you want to know more about the auto generated classes, have a look at this class: cache/[app]/[env]/modules/auto[ModuleName]/actions/actions.class.php.
Edit after comments:
I think you looked at the wrong class. Look here: cache/[app]/[env]/modules/auto[ModuleName]/actions/actions.class.php.
I set up a Propel project to check it and the method that is interesting for you is:
protected function buildCriteria()
{
if (is_null($this->filters))
{
$this->filters = $this->configuration->getFilterForm($this->getFilters());
}
$criteria = $this->filters->buildCriteria($this->getFilters());
$this->addSortCriteria($criteria);
$event = $this->dispatcher->filter(new sfEvent($this, 'admin.build_criteria'), $criteria);
$criteria = $event->getReturnValue();
return $criteria;
}
I also posted the whole content of this class to pastebin. It is a lot, the function is in line 245. Even if you don't find this class, you should be able to override this method like this:
protected function buildCriteria()
{
$criteria = parent::buildCriteria();
// do something with it
return $criteria;
}
I don't know about these criteria objects, so I can't help you with that but I hope the other things help you.

You should use sfGuardPlugin to provide your login/user functionality - it includes user groups and permissions that can be assigned to users and/or groups.
Using security.yml you can then configure which permissions/credentials are required to access individual actions. IE: you can allow everyone to access the list/update/delete actions, but only people with the create permission to access the create page.
The docs for sfGuardPlugin are worth reading:
http://www.symfony-project.org/plugins/sfGuardPlugin
Plus this section from the jobeet tutorial covers sfGuard and also use of security.yml and credentials:
http://www.symfony-project.org/jobeet/1_2/Propel/en/13
And to round off, this page from the book is relevant too:
http://www.symfony-project.org/reference/1_2/en/08-Security (although not sure it covers anything that isn't in the page i linked from jobeet)

Related

How can I use group membership to restrict results?

In an MVC application, for a given action that all users are authorized to perform, I would like to filter results based on the user's group membership. For instance ...
Users in GroupA should only see records pertaining to BuildingX.
Users in GroupB should only see records pertaining to BuildingY.
Users in GroupC should see all records.
I have no problem using authorization filters to restrict access to Actions, but I'm having a much harder time finding how to restrict access to data short of explicitly modifying statements every place where data is fetched.
Assuming your records are in a database, the roles membership model doesn't extend to the database out of the box. You can build a roles-based access control for your database, but you will likely save time using a simpler approach. For example, using code like this in your controller:
if (Roles.IsUserInRole("GroupA")) {
// Get data for GroupA.
}
// Display data...
A year later, working on a different but related issue, I found the EntityFramework.DynamicFilters package which does exactly what I need!

Is it possible to set a scope to a domain class?

I'd like to know if it's possible with grails to specify a scope for the domain classes.
Few words to explain how my application is working at the moment:
- database access is done through an external "module" using SQLJ. This module is user by controllers in my grails app.
- a user ask for specific information submitting forms -> request submitted to the external module -> information extracted from the database -> information loaded into grails mem DB (HSQL) -> information displayed in views.
It works fine in development environment as i'm the only one using the application. But i'm wondering how the application would behave with two or more users. I mean, do the information loaded into grails memory database will be shared between users or not? And how not to shared information requested by one user with the others?
Thanks in advance for any help about this subject.
Regards.
All data in the database is shared across all users of the grails application. You would have to write a custom query to limit the data returned to a specific user. Based on your application maybe something similar to the following.
class DomainClass1 {
//fields you get from SQLJ go here
int userId
}
To get data into an instance of your domain class.
def domInstance=new DomainClass1()
domInstance.loadFromSQLJ() //call the SQLJ module and put it's data in the domain class
domInstance.userId=5 //assign the user associated with this info
domInstance.save()
Then when you want to display info for the user with the userId 5
def domInstance2=DomainClass1.findByUserId(5)
//Do stuff with domInstance2
It will be shared between all users.
But it depends on you, as for any other database, there must be some criteria (db column) by which you can choose only information related to current user.
In our project, we overrode domain classes' get(), list() that take into account domain aggregate root (a User or whatever), and also check all the named queries.
This leaves off all the other means of accessing instances, like findBy*(), criteria, findWhere() (though you can also override the dynamic methods), or HQL, but anyway reduces the amount of security review by 80%.
Suddenly it turned out to be OK to use DomainClass.list() in scaffolding.

add user define properties to a domain class

i have a requirement to allow the user to define some custom field in one of the system entities. do you have any suggestion/pattern/plugin that will help me add this feature to my application.
thanks,
Meni
You can add a Map property to your domain class and store arbitrary data there. It's rather limited though. It will generate a table with varchar(255) keys and values, so you need to manage any type conversions yourself, e.g.
class Thing {
String name
Map extraProperties = [:]
}
int age = 123
def thing = new Thing(name: 'whatever')
thing.extraProperties.age = age.toString()
thing.save()
...
def thing = Thing.get(thingId)
int age = thing.extraProperties.age.toInteger()
See section "5.2.4 Sets, Lists and Maps" at http://grails.org/doc/latest/ for the brief online docs.
Sounds like you want your application to be an infinitely adjustable wrench that users can modify at will. Is that fair?
I don't think it's possible or desirable. Think about what happens when you add an attribute to an existing domain object in Grails. The attribute is added to the ORM mapping, which means the tables have to be modified. The UI has another text box added for data entry; the list page has another column added to its table.
There's a lot going on when you add an attribute. How will you manage multiple users modifying the app all at the same time? What happens when one user is modifying a table while another is accessing the old version?
You ask too much. I don't think it's a reasonable requirement. Grails' sweet spot is rapid development of web-based CRUD applications. I don't think that includes modification by users at runtime.

How can I modify the queryset in the change list view depending on a parameter I set in the URL

My problem is the following and it is related to the change list view of the admin interface.
I have a workorder model with several fields to caracterize the work order.
They are : type, nature, scheduling_type (and others).
When I see the list view, I would like to be able to change the filter (thus be able to create complex ones depending on the values of the different fields of the workorder model - the ones above and dates for example).
I have found post showing how to modify the default queryset (using managers for example) but I can't find a post that will use a value that is given in the url (ex. admin/workorder/planned_corrective). When the parameter planned_corrective is found, it must be used to select the appropriate queryset or manager and render the corresponding list.
As a add on, I want from that list to be able to use the standard admin options (like list filters, search ...) on that query.
Hope it is clear and thanks in advance for your help.
It sounds like you're after a RESTful interface.
You could accomplish much of this just by being clever with your urls.py - ie, defining admin/workoder/planned_corrective and every other possible parameter that could be encoded in the URL.
A lot of this can also be accomplished just by adding a get-absolute-url method to your models.
Or, you could the effort into using something like the django-rest-interface in your app.

ACL on field level in Grails

in our new software project, we have the following requirement: A webpage shall show a set of data. This data shall be editable by some users (assigned to roles, i.e. manager), and only viewable by others. The tricky part is described by an example:
A User-page consists of address data and account information. The addess data shall be editable by the user and the manager and viewable by all users, while account information shall only be viewable by the actual user and the manager.
I have read a lot of information about SpringSecurity. It provides a very good framework to gran permissions on urls and methods and even domain classes. But what I need is field level ACLs. At least, that's what I think at the moment.
So, the question is: How to solve this problem using Grails?
Thanks a lot in advance,
Regards Daniel
Spring Security (Acegi Plugin) is definitely the way to go with Grails.
There is a taglib you can use that will allow a page to be different for different roles such as the following:
<g:ifUserHasRole roles="ROLE_ADMIN">
html code for extra fields
</g:ifUserHasRole>
Me, I'd encode it on the domain class, emulating the way GORM has you annotate the domain classes (static access = [field1: "ROLE_USER", field2: "ROLE_ADMIN,ROLE_USER"] as an example). Then build a method your controller could use to redact them for a given user. That method could use the domain class's annotations to decide how to redact it. Then, metaprogram it onto each of the domain classes the way plugins do.
Similarly, write the opposite method to restrict data bindings of params into the domain class, write your own data binding utility method, then metaprogram it onto each domain class as well.
Then you can just use instance.redact(user) or instance.bindData(params, user) to do what you want, and it's practically declarative syntax.
We have a similar situation and use both the ifUserHasRole tag in the gsp to drive the appropriate presentation and the we have a filter that enforces the rules based on the action being called. For example, on user controller we would only allow the management roles to call save action, or if the user.id is the same as the session.user.id. This seemed to be the best option for our situation.
What about creating an ACL class like this:
class ACL(val entry: Entry*) {
def isAccessAllowed(subject: String, permission: String): Boolean = ...
}
class Entry(val subject: String, val permission: String*)
usage:
new ACL(
new Entry("dave", "read", "write"),
new Entry("linda", "read")
)
(This example is in Scala, because I found it more expressive in this case, but it should be easy to transfer it to Groovy.)
You would then connect an ACL object with the object to be protected.

Resources