Making fields readonly by group

Asked by Martin Collins

I want to control whether a field is readonly or not according to the user's group.
This is a common request on the forum but the only solution offered is to hack a copy of fields_get() into your module.
As far as I can see attrs won't work, alternative forms doesn't work either (or is there a way?), record rules only work on the record level (duh!) and likewise menuitem access controls.
Will the new modifiers allow this? Is there any other non-hacky way?

Question information

Language:
English Edit question
Status:
Expired
For:
Odoo Server (MOVED TO GITHUB) Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:

This question was reopened

Revision history for this message
Marco Dieckhoff (dieck) said :
#1

Hi!

"attrs" would kind of work, but only on view definitions.
If you access the data via a modified view (*1), another module (*1) or xmlrpc (*2), this would be cirumvented.

*1 You would need administrator rights to do so, so no problem, as you can't hide anything from admins, either way.
*2 For XMLRPC, user rights apply, so this is your problem.

Other than that, I think a modified fields_get() looks like the only way at this moment (at least in v5 or 6.0, I've yet to start with 6.1)

Revision history for this message
James Jesudason (jamesj) said :
#2

Hi,

There is another way: use a function field that returns a value based on the user's group. I did the following as a proof-of-concept, a while back:

Declare the function field:
        'permissions': fields.function(_check_permissions, type='char', method=True, string="Permissions"),

The function (which needs tidying) returns 'User' or 'Manager' depending on whether they are in a couple of groups:

  def _check_permissions(self, cr, uid, ids, field_name, arg, context):
   res = {}

   for i in ids:
    if not i:
     continue

    # Get the HR Officer/Manager id's
    group_obj = self.pool.get('res.groups')
    manager_ids = group_obj.search(cr, uid, ['|',('name','=', 'Human Resources / HR Officer'),('name','=', 'Human Resources / HR Manager')])

    # Get the user and see what groups he/she is in
    user_obj = self.pool.get('res.users')
    user = user_obj.browse(cr, uid, uid, context=context)

    group_ids = []
    for grp in user.groups_id:
     group_ids.append(grp.id)

    if (manager_ids[0] in group_ids) or (manager_ids[1] in group_ids):
     res[i] ='Manager'
    else:
     res[i] = 'User'

   return res

Revision history for this message
Martin Collins (mkc-steadfast) said :
#3

James, that works! Just have to test 'permissions' in an attr. And because it's a function field it doesn't clutter up the table. It could easily be adapted to test other properties too. All in all a very neat hack.

Revision history for this message
Martin Collins (mkc-steadfast) said :
#4

There is a caveat though: if the 'permissions' field is not included on a form it doesn't get calculated. You can hide it from some users using groups= and I guess setting store=True will work too. Setting attrs="{'invisible': something}" might work but I've not tried it.

Revision history for this message
Marco Dieckhoff (dieck) said :
#5

Hm. I don't think this permissions function field solves the problem.
This way, you get some information about which kind of permission the user has.
But that's just a coded version of using the user groups directly.

And it won't protect against write accesses - any user could still use XMLRPC to write to the database.

Revision history for this message
Martin Collins (mkc-steadfast) said :
#6

Any user with write perms on the model could use XMLRPC to write to the database, yes. In fact I have one that does (it's a weighing machine). Everyone else sees readonly fields, but sometimes an error is made and a manager needs to correct it.
At present I have to go into the database and edit it manually. I'm not overly concerned that my users may suddenly develop the skills to write XMLRPC code, though it would be nice if there was more protection.
I have heard tell of mysterious new modifiers and even field-level perms but I'm running 6.03 and see no sign of such.

Revision history for this message
digitalsatori(Shine IT) (digitalsatori) said :
#7

you can certainly assign access rule on field level in OpenERP, please
refer to the below example.

'name': fields.char('Name', size=128, required=True, select=True,
write=['base.group_admin'],read=['base.group_admin'] ),

Tony Gu

On Wed, Nov 16, 2011 at 6:25 AM, Martin Collins
<email address hidden> wrote:
> Question #178779 on OpenERP Server changed:
> https://answers.launchpad.net/openobject-server/+question/178779
>
> Martin Collins posted a new comment:
> Any user with write perms on the model could use XMLRPC to write to the database, yes. In fact I have one that does (it's a weighing machine). Everyone else sees readonly fields, but sometimes an error is made and a manager needs to correct it.
> At present I have to go into the database and edit it manually.  I'm not overly concerned that my users may suddenly develop the skills to write XMLRPC code, though it would be nice if there was more protection.
> I have heard tell of mysterious new modifiers and even field-level perms but I'm running 6.03 and see no sign of such.
>
> --
> You received this question notification because you are a member of
> OpenERP Committers, which is an answer contact for OpenERP Server.
>

Revision history for this message
James Jesudason (jamesj) said :
#8

The 'permissions' field can be used to make the field read-only in the view. It would need to be declared in the view e.g.
  <field name="permissions" invisible="1" />
  <field name="other_field" attrs="{'readonly':[('permissions','!=','Manager')]" />

It's probably not an efficient approach, but it works.

Stopping the field from being written to via XMLRPC is a separate issue, but easily solvable by overriding the 'create' and 'write' methods in the Python code.

> you can certainly assign access rule on field level in OpenERP, please
> refer to the below example.
>
> 'name': fields.char('Name', size=128, required=True, select=True,
> write=['base.group_admin'],read=['base.group_admin'] ),

Really? That's a much better solution if it works. I've not seen any documentation on this or examples of this approach in the code. Ideally, this is the way I think that OpenERP *should* work.

Revision history for this message
Martin Collins (mkc-steadfast) said :
#9

> 'name': fields.char('Name', size=128, required=True, select=True,
> write=['base.group_admin'],read=['base.group_admin'] ),

Do you have any more info on this, I'm not getting it.

Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) said :
#10

Ok, I see 3 ways with code adding :
1st way
'client_order_ref': fields.char('Customer Reference', size=64,write=['base.group_partner_manager'],read=['base.group_user']),
2nd way:
changes on the functions fields_view_get and/or fields_get:
set readonly attribute on precise fields depending on the uid's groups.
...but it would be a great feature to configure this on OpenERP directly by the Administrator user.
3rd way:
add permisssions functional field, then use attrs attributes on fields to control.
https://answers.launchpad.net/openobject-server/+question/178779

But the ideal would be the possibility for an Administrator user to configure directly it:
What do you think about this feature? For the most experienced among you, what do you think just on the technical potential compatibility with the core actual working of OE 6.1?
Is there any logical core limits to imagine adding this feature?

Revision history for this message
Martin Collins (mkc-steadfast) said :
#11

The '3rd way' no longer works in 6.1 and I still can't get the '1st way' working.
SerpentCS have a blog posting about this in 7.0 but from the description I cannot see how it would be useful even if I was running 7.
Anyone come up with a working solution yet?

Revision history for this message
Launchpad Janitor (janitor) said :
#12

This question was expired because it remained in the 'Open' state without activity for the last 15 days.

Revision history for this message
Martin Collins (mkc-steadfast) said :
#13

I got the '3rd way' working again. Before I was not explicitly setting the return value to False. It worked in 6.0 but in 6.1 you need to set it.

Revision history for this message
stone.wu (sutono-wu) said :
#14

I'd to use 1st way on 6.0, but after i implemented, it didn't have any effect
what i did is change on .py file

something like :
'name': fields.char('Description', size=256, required=True, write=['base.director'],read='base.user']),

do i missing something here?

Revision history for this message
Etienne Hirt (hirt) said :
#15

it would be very convenient if the attrs would also work based on the groups. It doesn't seem to work in 6.0 and 6.1?