On Mon, 2011-11-14 at 19:10 +0200, Alexander Bokovoy wrote:
> On Mon, 14 Nov 2011, Martin Kosek wrote:
> > Tests are now OK, there were a lot of errors related to permissions and
> > hosts (--random was always on).
> > 
> > I was also thinking if the encoding step shouldn't be done as late as
> > possible, i.e. after plugin's pre_callbacks, right before LDAP is
> > called. Now, we do encoding right in the beginning of command processing
> > and therefore plugin pre_callback already see the encoded values.
> > 
> > If we choose the late-encoding change, we would have to add the encoding
> > step before every LDAP call in baseldap.py or even before custom LDAP
> > calls in user plugins when execute() is overloaded - or write a wrapper
> > for the LDAP calls. That's why I rather chose the proposed solution.
> I don't like it but on further thinking it seems to unavoidable.
> One minor suggestion is to select more descriptive name because 
> 'attribute' and 'noattribute' are confusing, to say at least.
> 
> Or maybe we should document them better? Right now there is no 
> documentation for a majority of those flags.
> 

I renamed it to 'virtual_attribute' (as an opposite to physical LDAP
attribute), I hope it makes more sense.

You are right that we miss a documentation for most of Param attributes
and classes which may confuse especially newcomers to FreeIPA project. I
added a quick overview to Param class doc.

Martin
>From 5b55ccfc9d6e1f090c337ff06c44bfeeb497bfb3 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Mon, 14 Nov 2011 17:03:44 +0100
Subject: [PATCH] Fix LDAP object parameter encoding

Parameters in LDAP objects missed an information if they are real
LDAP attributes or not. Real LDAP attributes are written to
entry_attrs dictionary in plugin callbacks and are being encoded.
This causes issues when plugin callbacks does not expect that
the parameters values are already encoded for submission to LDAP.

This patch introduces a new flag "noattribute" used to mark that
a parameter is not an LDAP attribute and thus should not be encoded
or added to entry_attrs. Param documentation is improved to describe
the meaning of this and other Param flags or attributes.

https://fedorahosted.org/freeipa/ticket/2097
---
 API.txt                    |  108 ++++++++++++++++++++++----------------------
 ipalib/crud.py             |   19 +++++---
 ipalib/parameters.py       |   62 +++++++++++++++++++++++++
 ipalib/plugins/aci.py      |   11 +++++
 ipalib/plugins/dns.py      |    9 +---
 ipalib/plugins/host.py     |   22 ++++-----
 ipalib/plugins/pwpolicy.py |    4 +-
 7 files changed, 149 insertions(+), 86 deletions(-)

diff --git a/API.txt b/API.txt
index bafff6c0308985a5ebf87667220a52b82f168870..7a7cb008e5516d429b917d210cfca0f898bf5bd2 100644
--- a/API.txt
+++ b/API.txt
@@ -1,16 +1,16 @@
 command: aci_add
 args: 1,15,3
-arg: Str('aciname', attribute=True, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, required=True)
-option: Str('permission', attribute=True, cli_name='permission', label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('group', attribute=True, cli_name='group', label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: List('permissions', validate_permissions, attribute=True, cli_name='permissions', label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=True)
-option: List('attrs', attribute=True, cli_name='attrs', label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
-option: StrEnum('type', attribute=True, cli_name='type', label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
-option: Str('memberof', attribute=True, cli_name='memberof', label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('filter', attribute=True, cli_name='filter', label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('subtree', attribute=True, cli_name='subtree', label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('targetgroup', attribute=True, cli_name='targetgroup', label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Flag('selfaci', attribute=True, autofill=True, cli_name='self', default=False, label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
+arg: Str('aciname', attribute=True, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, required=True)
+option: Str('permission', attribute=False, cli_name='permission', flags=('virtual_attribute',), label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('group', attribute=False, cli_name='group', flags=('virtual_attribute',), label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: List('permissions', validate_permissions, attribute=False, cli_name='permissions', flags=('virtual_attribute',), label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=True)
+option: List('attrs', attribute=False, cli_name='attrs', flags=('virtual_attribute',), label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
+option: StrEnum('type', attribute=False, cli_name='type', flags=('virtual_attribute',), label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('memberof', attribute=False, cli_name='memberof', flags=('virtual_attribute',), label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('filter', attribute=False, cli_name='filter', flags=('virtual_attribute',), label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('subtree', attribute=False, cli_name='subtree', flags=('virtual_attribute',), label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('targetgroup', attribute=False, cli_name='targetgroup', flags=('virtual_attribute',), label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Flag('selfaci', attribute=False, autofill=True, cli_name='self', default=False, flags=('virtual_attribute',), label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
 option: StrEnum('aciprefix', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), values=(u'permission', u'delegation', u'selfservice', u'none'))
 option: Flag('test?', autofill=True, default=False)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
@@ -21,7 +21,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
 command: aci_del
 args: 1,1,3
-arg: Str('aciname', attribute=True, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('aciname', attribute=True, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
 option: StrEnum('aciprefix', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), values=(u'permission', u'delegation', u'selfservice', u'none'))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed')
 output: Output('result', <type 'bool'>, 'True means the operation was successful')
@@ -29,17 +29,17 @@ output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e
 command: aci_find
 args: 1,15,4
 arg: Str('criteria?')
-option: Str('aciname', attribute=True, autofill=False, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=False)
-option: Str('permission', attribute=True, autofill=False, cli_name='permission', label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Str('group', attribute=True, autofill=False, cli_name='group', label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: List('permissions', validate_permissions, attribute=True, autofill=False, cli_name='permissions', label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, query=True, required=False)
-option: List('attrs', attribute=True, autofill=False, cli_name='attrs', label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, query=True, required=False)
-option: StrEnum('type', attribute=True, autofill=False, cli_name='type', label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
-option: Str('memberof', attribute=True, autofill=False, cli_name='memberof', label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Str('filter', attribute=True, autofill=False, cli_name='filter', label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Str('subtree', attribute=True, autofill=False, cli_name='subtree', label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Str('targetgroup', attribute=True, autofill=False, cli_name='targetgroup', label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Bool('selfaci', attribute=True, autofill=False, cli_name='self', default=False, label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('aciname', attribute=False, autofill=False, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=False)
+option: Str('permission', attribute=False, autofill=False, cli_name='permission', flags=('virtual_attribute',), label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('group', attribute=False, autofill=False, cli_name='group', flags=('virtual_attribute',), label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: List('permissions', validate_permissions, attribute=False, autofill=False, cli_name='permissions', flags=('virtual_attribute',), label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, query=True, required=False)
+option: List('attrs', attribute=False, autofill=False, cli_name='attrs', flags=('virtual_attribute',), label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, query=True, required=False)
+option: StrEnum('type', attribute=False, autofill=False, cli_name='type', flags=('virtual_attribute',), label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('memberof', attribute=False, autofill=False, cli_name='memberof', flags=('virtual_attribute',), label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('filter', attribute=False, autofill=False, cli_name='filter', flags=('virtual_attribute',), label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('subtree', attribute=False, autofill=False, cli_name='subtree', flags=('virtual_attribute',), label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('targetgroup', attribute=False, autofill=False, cli_name='targetgroup', flags=('virtual_attribute',), label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Bool('selfaci', attribute=False, autofill=False, cli_name='self', default=False, flags=('virtual_attribute',), label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
 option: StrEnum('aciprefix?', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'permission', u'delegation', u'selfservice', u'none'))
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output'])
@@ -50,17 +50,17 @@ output: Output('count', <type 'int'>, 'Number of entries returned')
 output: Output('truncated', <type 'bool'>, 'True if not all results were returned')
 command: aci_mod
 args: 1,14,3
-arg: Str('aciname', attribute=True, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
-option: Str('permission', attribute=True, autofill=False, cli_name='permission', label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('group', attribute=True, autofill=False, cli_name='group', label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: List('permissions', validate_permissions, attribute=True, autofill=False, cli_name='permissions', label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=False)
-option: List('attrs', attribute=True, autofill=False, cli_name='attrs', label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
-option: StrEnum('type', attribute=True, autofill=False, cli_name='type', label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
-option: Str('memberof', attribute=True, autofill=False, cli_name='memberof', label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('filter', attribute=True, autofill=False, cli_name='filter', label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('subtree', attribute=True, autofill=False, cli_name='subtree', label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('targetgroup', attribute=True, autofill=False, cli_name='targetgroup', label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Flag('selfaci', attribute=True, autofill=True, cli_name='self', default=False, label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
+arg: Str('aciname', attribute=True, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
+option: Str('permission', attribute=False, autofill=False, cli_name='permission', flags=('virtual_attribute',), label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('group', attribute=False, autofill=False, cli_name='group', flags=('virtual_attribute',), label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: List('permissions', validate_permissions, attribute=False, autofill=False, cli_name='permissions', flags=('virtual_attribute',), label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=False)
+option: List('attrs', attribute=False, autofill=False, cli_name='attrs', flags=('virtual_attribute',), label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
+option: StrEnum('type', attribute=False, autofill=False, cli_name='type', flags=('virtual_attribute',), label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('memberof', attribute=False, autofill=False, cli_name='memberof', flags=('virtual_attribute',), label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('filter', attribute=False, autofill=False, cli_name='filter', flags=('virtual_attribute',), label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('subtree', attribute=False, autofill=False, cli_name='subtree', flags=('virtual_attribute',), label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('targetgroup', attribute=False, autofill=False, cli_name='targetgroup', flags=('virtual_attribute',), label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Flag('selfaci', attribute=False, autofill=True, cli_name='self', default=False, flags=('virtual_attribute',), label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
 option: StrEnum('aciprefix', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), values=(u'permission', u'delegation', u'selfservice', u'none'))
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output'])
@@ -70,17 +70,17 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
 command: aci_rename
 args: 1,15,3
-arg: Str('aciname', attribute=True, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
-option: Str('permission', attribute=True, autofill=False, cli_name='permission', label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('group', attribute=True, autofill=False, cli_name='group', label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: List('permissions', validate_permissions, attribute=True, autofill=False, cli_name='permissions', label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=False)
-option: List('attrs', attribute=True, autofill=False, cli_name='attrs', label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
-option: StrEnum('type', attribute=True, autofill=False, cli_name='type', label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
-option: Str('memberof', attribute=True, autofill=False, cli_name='memberof', label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('filter', attribute=True, autofill=False, cli_name='filter', label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('subtree', attribute=True, autofill=False, cli_name='subtree', label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('targetgroup', attribute=True, autofill=False, cli_name='targetgroup', label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Flag('selfaci', attribute=True, autofill=True, cli_name='self', default=False, label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
+arg: Str('aciname', attribute=True, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
+option: Str('permission', attribute=False, autofill=False, cli_name='permission', flags=('virtual_attribute',), label=Gettext('Permission', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('group', attribute=False, autofill=False, cli_name='group', flags=('virtual_attribute',), label=Gettext('User group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: List('permissions', validate_permissions, attribute=False, autofill=False, cli_name='permissions', flags=('virtual_attribute',), label=Gettext('Permissions', domain='ipa', localedir=None), multivalue=True, normalizer=_normalize_permissions, required=False)
+option: List('attrs', attribute=False, autofill=False, cli_name='attrs', flags=('virtual_attribute',), label=Gettext('Attributes', domain='ipa', localedir=None), multivalue=True, required=False)
+option: StrEnum('type', attribute=False, autofill=False, cli_name='type', flags=('virtual_attribute',), label=Gettext('Type', domain='ipa', localedir=None), multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('memberof', attribute=False, autofill=False, cli_name='memberof', flags=('virtual_attribute',), label=Gettext('Member of', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('filter', attribute=False, autofill=False, cli_name='filter', flags=('virtual_attribute',), label=Gettext('Filter', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('subtree', attribute=False, autofill=False, cli_name='subtree', flags=('virtual_attribute',), label=Gettext('Subtree', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('targetgroup', attribute=False, autofill=False, cli_name='targetgroup', flags=('virtual_attribute',), label=Gettext('Target group', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Flag('selfaci', attribute=False, autofill=True, cli_name='self', default=False, flags=('virtual_attribute',), label=Gettext('Target your own entry (self)', domain='ipa', localedir=None), multivalue=False, required=False)
 option: StrEnum('aciprefix', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), values=(u'permission', u'delegation', u'selfservice', u'none'))
 option: Str('newname')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
@@ -91,7 +91,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
 command: aci_show
 args: 1,4,3
-arg: Str('aciname', attribute=True, cli_name='name', label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
+arg: Str('aciname', attribute=True, cli_name='name', flags=('virtual_attribute',), label=Gettext('ACI name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
 option: StrEnum('aciprefix', cli_name='prefix', label=Gettext('ACI prefix', domain='ipa', localedir=None), values=(u'permission', u'delegation', u'selfservice', u'none'))
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output'])
@@ -856,7 +856,7 @@ output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e
 command: dnszone_add
 args: 1,19,3
 arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, required=True)
-option: Str('name_from_ip', _validate_ipnet, attribute=True, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('name_from_ip', _validate_ipnet, attribute=False, cli_name='name_from_ip', flags=('virtual_attribute',), label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('idnssoamname', attribute=True, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=True)
 option: Str('idnssoarname', _rname_validator, attribute=True, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=True)
 option: Int('idnssoaserial', attribute=True, autofill=True, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
@@ -901,7 +901,7 @@ command: dnszone_find
 args: 1,21,4
 arg: Str('criteria?', noextrawhitespace=False)
 option: Str('idnsname', attribute=True, autofill=False, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=False)
-option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
+option: Str('name_from_ip', _validate_ipnet, attribute=False, autofill=False, cli_name='name_from_ip', flags=('virtual_attribute',), label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
 option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
 option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, query=True, required=False)
 option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, query=True, required=False)
@@ -928,7 +928,7 @@ output: Output('truncated', <type 'bool'>, 'True if not all results were returne
 command: dnszone_mod
 args: 1,18,3
 arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=True)
-option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
+option: Str('name_from_ip', _validate_ipnet, attribute=False, autofill=False, cli_name='name_from_ip', flags=('virtual_attribute',), label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=False)
 option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
@@ -1467,7 +1467,7 @@ option: Str('nshostlocation', attribute=True, cli_name='location', label=Gettext
 option: Str('nshardwareplatform', attribute=True, cli_name='platform', label=Gettext('Platform', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('nsosversion', attribute=True, cli_name='os', label=Gettext('Operating system', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('userpassword', attribute=True, cli_name='password', label=Gettext('User password', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Flag('random', attribute=True, autofill=True, cli_name='random', default=False, flags=['no_search'], label=FixMe('random'), multivalue=False, required=False)
+option: Flag('random', attribute=False, autofill=True, cli_name='random', default=False, flags=('no_search', 'virtual_attribute'), label=FixMe('random'), multivalue=False, required=False)
 option: Bytes('usercertificate', validate_certificate, attribute=True, cli_name='certificate', label=Gettext('Certificate', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Flag('force', autofill=True, default=False, label=Gettext('Force', domain='ipa', localedir=None))
 option: Flag('no_reverse', autofill=True, default=False)
@@ -1545,7 +1545,7 @@ option: Str('nshostlocation', attribute=True, autofill=False, cli_name='location
 option: Str('nshardwareplatform', attribute=True, autofill=False, cli_name='platform', label=Gettext('Platform', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('nsosversion', attribute=True, autofill=False, cli_name='os', label=Gettext('Operating system', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('userpassword', attribute=True, autofill=False, cli_name='password', label=Gettext('User password', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Flag('random', attribute=True, autofill=True, cli_name='random', default=False, flags=['no_search'], label=FixMe('random'), multivalue=False, required=False)
+option: Flag('random', attribute=False, autofill=True, cli_name='random', default=False, flags=('no_search', 'virtual_attribute'), label=FixMe('random'), multivalue=False, required=False)
 option: Bytes('usercertificate', validate_certificate, attribute=True, autofill=False, cli_name='certificate', label=Gettext('Certificate', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('addattr*', validate_add_attribute, cli_name='addattr', exclude='webui')
 option: Str('setattr*', validate_set_attribute, cli_name='setattr', exclude='webui')
@@ -2065,7 +2065,7 @@ option: Int('krbminpwdlife', attribute=True, cli_name='minlife', label=Gettext('
 option: Int('krbpwdhistorylength', attribute=True, cli_name='history', label=Gettext('History size', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdmindiffchars', attribute=True, cli_name='minclasses', label=Gettext('Character classes', domain='ipa', localedir=None), maxvalue=5, minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdminlength', attribute=True, cli_name='minlength', label=Gettext('Min length', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
-option: Int('cospriority', attribute=True, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=True)
+option: Int('cospriority', attribute=False, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), flags=('virtual_attribute',), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=True)
 option: Int('krbpwdmaxfailure', attribute=True, cli_name='maxfail', label=Gettext('Max failures', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdfailurecountinterval', attribute=True, cli_name='failinterval', label=Gettext('Failure reset interval', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdlockoutduration', attribute=True, cli_name='lockouttime', label=Gettext('Lockout duration', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
@@ -2093,7 +2093,7 @@ option: Int('krbminpwdlife', attribute=True, autofill=False, cli_name='minlife',
 option: Int('krbpwdhistorylength', attribute=True, autofill=False, cli_name='history', label=Gettext('History size', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
 option: Int('krbpwdmindiffchars', attribute=True, autofill=False, cli_name='minclasses', label=Gettext('Character classes', domain='ipa', localedir=None), maxvalue=5, minvalue=0, multivalue=False, query=True, required=False)
 option: Int('krbpwdminlength', attribute=True, autofill=False, cli_name='minlength', label=Gettext('Min length', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
-option: Int('cospriority', attribute=True, autofill=False, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
+option: Int('cospriority', attribute=False, autofill=False, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), flags=('virtual_attribute',), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
 option: Int('krbpwdmaxfailure', attribute=True, autofill=False, cli_name='maxfail', label=Gettext('Max failures', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
 option: Int('krbpwdfailurecountinterval', attribute=True, autofill=False, cli_name='failinterval', label=Gettext('Failure reset interval', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
 option: Int('krbpwdlockoutduration', attribute=True, autofill=False, cli_name='lockouttime', label=Gettext('Lockout duration', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
@@ -2115,7 +2115,7 @@ option: Int('krbminpwdlife', attribute=True, autofill=False, cli_name='minlife',
 option: Int('krbpwdhistorylength', attribute=True, autofill=False, cli_name='history', label=Gettext('History size', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdmindiffchars', attribute=True, autofill=False, cli_name='minclasses', label=Gettext('Character classes', domain='ipa', localedir=None), maxvalue=5, minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdminlength', attribute=True, autofill=False, cli_name='minlength', label=Gettext('Min length', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
-option: Int('cospriority', attribute=True, autofill=False, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
+option: Int('cospriority', attribute=False, autofill=False, cli_name='priority'('Priority of the policy (higher number means lower priority', domain='ipa', localedir=None), flags=('virtual_attribute',), label=Gettext('Priority', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdmaxfailure', attribute=True, autofill=False, cli_name='maxfail', label=Gettext('Max failures', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdfailurecountinterval', attribute=True, autofill=False, cli_name='failinterval', label=Gettext('Failure reset interval', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('krbpwdlockoutduration', attribute=True, autofill=False, cli_name='lockouttime', label=Gettext('Lockout duration', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
diff --git a/ipalib/crud.py b/ipalib/crud.py
index 97d6430d70f74cacdb7d78d5157a4b1a62ea9405..833914cfaa2dd4e99afae8065651435b0bc0f898 100644
--- a/ipalib/crud.py
+++ b/ipalib/crud.py
@@ -139,15 +139,16 @@ class Create(Method):
             for option in super(Create, self).get_options():
                 yield option
         for option in self.obj.params_minus(self.args):
+            attribute = 'virtual_attribute' not in option.flags
             if 'no_create' in option.flags:
                 continue
             if 'ask_create' in option.flags:
                 yield option.clone(
-                    attribute=True, query=True, required=False,
+                    attribute=attribute, query=True, required=False,
                     autofill=False, alwaysask=True
                 )
             else:
-                yield option.clone(attribute=True)
+                yield option.clone(attribute=attribute)
         if not self.extra_options_first:
             for option in super(Create, self).get_options():
                 yield option
@@ -183,19 +184,20 @@ class Update(PKQuery):
             for option in super(Update, self).get_options():
                 yield option
         for option in self.obj.params_minus_pk():
+            attribute = 'virtual_attribute' not in option.flags
             if 'no_update' in option.flags:
                 continue
             if 'ask_update' in option.flags:
                 yield option.clone(
-                    attribute=True, query=True, required=False,
+                    attribute=attribute, query=True, required=False,
                     autofill=False, alwaysask=True
                 )
             elif 'req_update' in option.flags:
                 yield option.clone(
-                    attribute=True, required=True, alwaysask=False,
+                    attribute=attribute, required=True, alwaysask=False,
                 )
             else:
-                yield option.clone(attribute=True, required=False, autofill=False)
+                yield option.clone(attribute=attribute, required=False, autofill=False)
         if not self.extra_options_first:
             for option in super(Update, self).get_options():
                 yield option
@@ -224,21 +226,22 @@ class Search(Method):
             for option in super(Search, self).get_options():
                 yield option
         for option in self.obj.params_minus(self.args):
+            attribute = 'virtual_attribute' not in option.flags
             if 'no_search' in option.flags:
                 continue
             if 'ask_search' in option.flags:
                 yield option.clone(
-                    attribute=True, query=True, required=False,
+                    attribute=attribute, query=True, required=False,
                     autofill=False, alwaysask=True
                 )
             elif isinstance(option, parameters.Flag):
                 yield option.clone_retype(
                     option.name, parameters.Bool,
-                    attribute=True, query=True, required=False, autofill=False
+                    attribute=attribute, query=True, required=False, autofill=False
                 )
             else:
                 yield option.clone(
-                    attribute=True, query=True, required=False, autofill=False
+                    attribute=attribute, query=True, required=False, autofill=False
                 )
         if not self.extra_options_first:
             for option in super(Search, self).get_options():
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index 1f3fdfde7452dace6b13a534b9737e8c3b1b0e5d..96dde7fc25a96a2885d19c687b0fd77f678e3e59 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -285,6 +285,68 @@ def _(message):
 class Param(ReadOnly):
     """
     Base class for all parameters.
+
+    Param attributes:
+    =================
+    The behavior of Param class and subclasses can be controlled using the
+    following set of attributes:
+
+      - cli_name: option name in CLI
+      - cli_short_name: one character version of cli_name
+      - label: very short description of the parameter. This value is used in
+        when the Command output is printed to CLI or in a Command help
+      - doc: parameter long description used in help
+      - required: the parameter is marked as required for given Command
+      - multivalue: indicates if the attribute is multivalued
+      - primary_key: Command's parameter primary key is used for unique
+        identification of an LDAP object and for sorting
+      - normalizer: a custom function for Param value normalization
+      - encoder: a custom function used to override Param subclass default
+        encoder
+      - default_from: a custom function for generating default values of
+        parameter instance
+      - create_default: a custom function for generating default values of
+        parameter instance. Unlike default_from attribute, this function
+        is not wrapped. `Param.get_default()` documentation provides further
+        details
+      - autofill: by default, only `required` parameters get a default value
+        from default_from or create_default functions. When autofill is
+        enabled, optional attributes get the default value filled too
+      - query: this attribute is controlled by framework. When the `query`
+        is enabled, framework assumes that the value is only queried and not
+        inserted in the LDAP. Validation is then relaxed - custom
+        parameter validators are skipped and only basic class validators are
+        executed to check the parameter value
+      - attribute: this attribute is controlled by framework and enabled for
+        all LDAP objects parameters (unless parameter has "virtual_attribute"
+        flag). All parameters with enabled `attribute` are being encoded and
+        placed to an entry passed to LDAP Create/Update calls
+      - include: a list of contexts where this parameter should be included.
+        `Param.use_in_context()` provides further information.
+      - exclude: a list of contexts where this parameter should be excluded.
+        `Param.use_in_context()` provides further information.
+      - flags: there are several flags that can be used to further tune the
+        parameter behavior:
+            * no_display (Output parameters only): do not display the parameter
+            * no_create: do not include the parameter for crud.Create based
+              commands
+            * no_update: do not include the parameter for crud.update based
+              commands
+            * virtual_attribute: the parameter is not stored physically in the
+              LDAP and thus attribute `attribute` is not enabled
+            * suppress_empty (Output parameters only): do not display parameter
+              value when empty
+            * ask_create: CLI asks for parameter value even when the parameter
+              is not `required`. Applied for all crud.Create based commands
+            * ask_update: CLI asks for parameter value even when the parameter
+              is not `required`. Applied for all crud.Update based commands
+            * req_update: The parameter is `required` in all crud.Update based
+              commands
+      - hint: This attribute is currently not used
+      - alwaysask: when enabled, CLI asks for parameter value even when the
+        parameter is not `required`
+      - sortorder: used to sort a list of parameters for Command. See
+        `Command.finalize()` for further information
     """
 
     # This is a dummy type so that most of the functionality of Param can be
diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py
index 429ae6eb54214b0b7e0f8ff9f58150e090ec2f2b..585dab837edc9dd284869d6c061bb21b71bfbae2 100644
--- a/ipalib/plugins/aci.py
+++ b/ipalib/plugins/aci.py
@@ -416,16 +416,19 @@ class aci(Object):
             cli_name='name',
             label=_('ACI name'),
             primary_key=True,
+            flags=('virtual_attribute',),
         ),
         Str('permission?',
             cli_name='permission',
             label=_('Permission'),
             doc=_('Permission ACI grants access to'),
+            flags=('virtual_attribute',),
         ),
         Str('group?',
             cli_name='group',
             label=_('User group'),
             doc=_('User group ACI grants access to'),
+            flags=('virtual_attribute',),
         ),
         List('permissions', validate_permissions,
             cli_name='permissions',
@@ -433,42 +436,50 @@ class aci(Object):
             doc=_('comma-separated list of permissions to grant' \
                 '(read, write, add, delete, all)'),
             normalizer=_normalize_permissions,
+            flags=('virtual_attribute',),
         ),
         List('attrs?',
             cli_name='attrs',
             label=_('Attributes'),
             doc=_('Comma-separated list of attributes'),
+            flags=('virtual_attribute',),
         ),
         StrEnum('type?',
             cli_name='type',
             label=_('Type'),
             doc=_('type of IPA object (user, group, host, hostgroup, service, netgroup)'),
             values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'),
+            flags=('virtual_attribute',),
         ),
         Str('memberof?',
             cli_name='memberof',
             label=_('Member of'),  # FIXME: Does this label make sense?
             doc=_('Member of a group'),
+            flags=('virtual_attribute',),
         ),
         Str('filter?',
             cli_name='filter',
             label=_('Filter'),
             doc=_('Legal LDAP filter (e.g. ou=Engineering)'),
+            flags=('virtual_attribute',),
         ),
         Str('subtree?',
             cli_name='subtree',
             label=_('Subtree'),
             doc=_('Subtree to apply ACI to'),
+            flags=('virtual_attribute',),
         ),
         Str('targetgroup?',
             cli_name='targetgroup',
             label=_('Target group'),
             doc=_('Group to apply ACI to'),
+            flags=('virtual_attribute',),
         ),
         Flag('selfaci?',
              cli_name='self',
              label=_('Target your own entry (self)'),
              doc=_('Apply ACI to your own entry (self)'),
+             flags=('virtual_attribute',),
         ),
     )
 
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 0a0bcb79c8bfdc055e7e470fbdc7ba7a090aecbd..bfc8090ca5a9304c3c3b18c96a24087cce7fee86 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -665,6 +665,7 @@ class dnszone(LDAPObject):
         Str('name_from_ip?', _validate_ipnet,
             label=_('Reverse zone IP network'),
             doc=_('IP network to create reverse zone name from'),
+            flags=('virtual_attribute',),
         ),
         Str('idnssoamname',
             cli_name='name_server',
@@ -780,9 +781,6 @@ class dnszone_add(LDAPCreate):
         if not dns_container_exists(self.api.Backend.ldap2):
             raise errors.NotFound(reason=_('DNS is not configured'))
 
-        if 'name_from_ip' in entry_attrs:
-            del entry_attrs['name_from_ip']
-
         entry_attrs['idnszoneactive'] = 'TRUE'
 
         # Check nameserver has a forward record
@@ -832,11 +830,6 @@ class dnszone_mod(LDAPUpdate):
             self.obj.params['name_from_ip'](unicode(options['name_from_ip']))
         return super(dnszone_mod, self).args_options_2_params(*args, **options)
 
-    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
-        if 'name_from_ip' in entry_attrs:
-            del entry_attrs['name_from_ip']
-        return dn
-
 api.register(dnszone_mod)
 
 
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index 6557880aa82598857251f3d8b80e6d3b326fbca6..33d60ad1c421e5bdd79a61518c3a809d30f6cae1 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -290,12 +290,12 @@ class host(LDAPObject):
         ),
         Flag('random?',
             doc=_('Generate a random password to be used in bulk enrollment'),
-            flags=['no_search'],
+            flags=('no_search', 'virtual_attribute'),
             default=False,
         ),
         Str('randompassword?',
             label=_('Random password'),
-            flags=['no_create', 'no_update', 'no_search'],
+            flags=('no_create', 'no_update', 'no_search', 'virtual_attribute'),
         ),
         Bytes('usercertificate?', validate_certificate,
             cli_name='certificate',
@@ -432,12 +432,10 @@ class host_add(LDAPCreate):
                 entry_attrs['objectclass'].remove('krbprincipalaux')
             if 'krbprincipal' in entry_attrs['objectclass']:
                 entry_attrs['objectclass'].remove('krbprincipal')
-        if 'random' in options:
-            if options.get('random'):
-                entry_attrs['userpassword'] = ipa_generate_password()
-                # save the password so it can be displayed in post_callback
-                setattr(context, 'randompassword', entry_attrs['userpassword'])
-            del entry_attrs['random']
+        if options.get('random'):
+            entry_attrs['userpassword'] = ipa_generate_password()
+            # save the password so it can be displayed in post_callback
+            setattr(context, 'randompassword', entry_attrs['userpassword'])
         cert = options.get('usercertificate')
         if cert:
             cert = x509.normalize_certificate(cert)
@@ -680,11 +678,9 @@ class host_mod(LDAPUpdate):
                         raise nsprerr
 
             entry_attrs['usercertificate'] = cert
-        if 'random' in options:
-            if options.get('random'):
-                entry_attrs['userpassword'] = ipa_generate_password()
-                setattr(context, 'randompassword', entry_attrs['userpassword'])
-            del entry_attrs['random']
+        if options.get('random'):
+            entry_attrs['userpassword'] = ipa_generate_password()
+            setattr(context, 'randompassword', entry_attrs['userpassword'])
 
         return dn
 
diff --git a/ipalib/plugins/pwpolicy.py b/ipalib/plugins/pwpolicy.py
index 6c5e6c56b8332fe39cfbaf9b3766c32864fec111..db42bca0424da34bc17b7df376d529bd60f55751 100644
--- a/ipalib/plugins/pwpolicy.py
+++ b/ipalib/plugins/pwpolicy.py
@@ -263,6 +263,7 @@ class pwpolicy(LDAPObject):
             label=_('Priority'),
             doc=_('Priority of the policy (higher number means lower priority'),
             minvalue=0,
+            flags=('virtual_attribute',),
         ),
     ) + lockout_params
 
@@ -344,8 +345,6 @@ class pwpolicy_add(LDAPCreate):
             keys[-1], krbpwdpolicyreference=dn,
             cospriority=options.get('cospriority')
         )
-        if 'cospriority' in entry_attrs:
-            del entry_attrs['cospriority']
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@@ -406,7 +405,6 @@ class pwpolicy_mod(LDAPUpdate):
                     raise e
             else:
                 setattr(context, 'cosupdate', True)
-            del entry_attrs['cospriority']
         return dn
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
-- 
1.7.7.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to