[Freeipa-devel] [PATCH] Password vault

Endi Sukma Dewata edewata at redhat.com
Fri Jul 3 13:44:12 UTC 2015


Here is the rebased patch for vault access control.

-- 
Endi S. Dewata
-------------- next part --------------
>From 6bec99d51552a6415c45d655f95627e341fae44b Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata at redhat.com>
Date: Fri, 17 Oct 2014 12:05:34 -0400
Subject: [PATCH] Added vault access control.

New LDAP ACIs have been added to allow vault owners to manage the
vaults and to allow members to access the vaults. New CLIs have
been added to manage the owner and member list. The LDAP schema
has been updated as well.

https://fedorahosted.org/freeipa/ticket/3872
---
 API.txt                                   |  92 ++++++++++++++++++----
 VERSION                                   |   4 +-
 install/share/60basev3.ldif               |   2 +-
 install/share/vault.update                |   5 ++
 ipalib/plugins/vault.py                   | 122 ++++++++++++++++++++++++++++--
 ipatests/test_xmlrpc/test_vault_plugin.py |  27 +++++--
 6 files changed, 224 insertions(+), 28 deletions(-)

diff --git a/API.txt b/API.txt
index d0ae1b72c2ae445a4e2cc168da5fd53f9a4de56d..c182098fe1017d46f9f7980c7b6891a1031f1068 100644
--- a/API.txt
+++ b/API.txt
@@ -5341,27 +5341,58 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui
 option: Str('service?')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: vault_add_internal
-args: 1,10,3
+args: 1,11,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
 option: Bytes('ipavaultpublickey', attribute=True, cli_name='public_key', multivalue=False, required=False)
 option: Bytes('ipavaultsalt', attribute=True, cli_name='salt', multivalue=False, required=False)
 option: Str('ipavaulttype', attribute=True, autofill=True, cli_name='type', default=u'standard', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: vault_add_member
+args: 1,9,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('service?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('username?', cli_name='user')
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+command: vault_add_owner
+args: 1,9,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('service?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('username?', cli_name='user')
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: vault_archive
 args: 1,10,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
@@ -5373,7 +5404,7 @@ option: Str('password_file?', cli_name='password_file')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
@@ -5387,7 +5418,7 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui
 option: Str('service?')
 option: Bytes('session_key')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Bytes('vault_data')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
@@ -5399,32 +5430,33 @@ arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=True,
 option: Flag('continue', autofill=True, cli_name='continue', default=False)
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Output('result', <type 'dict'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfPrimaryKeys('value', None, None)
 command: vault_find
-args: 1,12,4
+args: 1,13,4
 arg: Str('criteria?', noextrawhitespace=False)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('cn', attribute=True, autofill=False, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False)
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
 option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, query=True, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Flag('pkey_only?', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
 option: Int('sizelimit?', autofill=False, minvalue=0)
 option: Int('timelimit?', autofill=False, minvalue=0)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Output('count', <type 'int'>, None)
 output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: vault_mod
-args: 1,14,3
+args: 1,15,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
@@ -5433,16 +5465,47 @@ option: Str('description', attribute=True, autofill=False, cli_name='desc', mult
 option: Bytes('ipavaultpublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, required=False)
 option: Bytes('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, required=False)
 option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
 option: Str('service?')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: vault_remove_member
+args: 1,9,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('service?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('username?', cli_name='user')
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+command: vault_remove_owner
+args: 1,9,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('service?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('username?', cli_name='user')
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: vault_retrieve
 args: 1,11,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
@@ -5455,7 +5518,7 @@ option: Str('private_key_file?', cli_name='private_key_file')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
@@ -5468,20 +5531,21 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui
 option: Str('service?')
 option: Bytes('session_key')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: vault_show
-args: 1,7,3
+args: 1,8,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
 option: Str('service?')
 option: Flag('shared?', autofill=True, default=False)
-option: Str('user?')
+option: Str('username?', cli_name='user')
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
diff --git a/VERSION b/VERSION
index f69a5bb880c1141b620159fa3e6ea6f0eb6a30fd..9b0dde3f7e4c70c50c47d4ac8c69de7f06affe51 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=137
-# Last change: edewata - added ipaVaultPublicKey attribute
+IPA_API_VERSION_MINOR=138
+# Last change: edewata - added vault access control
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 5491f99f5e78f122f94e9215bf5751d487f19d2e..cf32a4abc1b69c32af96a1f067d0b9126931b66c 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -82,4 +82,4 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap
 objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' )
 objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' )
 objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' )
-objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ ipaVaultType $ ipaVaultSalt $ ipaVaultPublicKey ) X-ORIGIN 'IPA v4.2' )
+objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ ipaVaultType $ ipaVaultSalt $ ipaVaultPublicKey $ owner $ member ) X-ORIGIN 'IPA v4.2' )
diff --git a/install/share/vault.update b/install/share/vault.update
index dcd1e2a152b82263f0a2653eb5604a3c76002dd9..a6c649ac12995359dd935675f28a22468cd7f355 100644
--- a/install/share/vault.update
+++ b/install/share/vault.update
@@ -7,6 +7,11 @@ dn: cn=vaults,cn=kra,$SUFFIX
 default: objectClass: top
 default: objectClass: nsContainer
 default: cn: vaults
+default: aci: (target="ldap:///cn=*,cn=users,cn=vaults,cn=kra,$SUFFIX")(version 3.0; acl "Allow users to create private container"; allow (add) userdn = "ldap:///uid=($$attr.cn),cn=users,cn=accounts,$SUFFIX";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(version 3.0; acl "Vault members can access the vault"; allow(read, search, compare) userattr="member#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(version 3.0; acl "Indirect vault members can access the vault"; allow(read, search, compare) userattr="member#GROUPDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(version 3.0; acl "Vault owners can manage the vault"; allow(read, search, compare, write) userattr="owner#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(version 3.0; acl "Indirect vault owners can manage the vault"; allow(read, search, compare, write) userattr="owner#GROUPDN";)
 
 dn: cn=services,cn=vaults,cn=kra,$SUFFIX
 default: objectClass: top
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 9fcd619d19de9ae036a73bb3af9dc050c6be6c76..c10510e63da0353511b788ac625c439fe85eb96b 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -42,7 +42,8 @@ from ipalib import output
 from ipalib.crud import PKQuery, Retrieve, Update
 from ipalib.plugable import Registry
 from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete,\
-    LDAPSearch, LDAPUpdate, LDAPRetrieve, pkey_to_value
+    LDAPSearch, LDAPUpdate, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember,\
+    pkey_to_value
 from ipalib.request import context
 from ipalib.plugins.user import split_principal
 from ipalib import _, ngettext
@@ -195,6 +196,18 @@ EXAMPLES:
 """) + _("""
  Retrieve data from asymmetric vault:
    ipa vault-retrieve <name> --out data.bin --private-key-file private.pem
+""") + _("""
+ Add a vault owner:
+   ipa vault-add-owner <name> --users <usernames>
+""") + _("""
+ Delete a vault owner:
+   ipa vault-remove-owner <name> --users <usernames>
+""") + _("""
+ Add a vault member:
+   ipa vault-add-member <name> --users <usernames>
+""") + _("""
+ Delete a vault member:
+   ipa vault-remove-member <name> --users <usernames>
 """)
 
 register = Registry()
@@ -210,7 +223,8 @@ vault_options = (
         doc=_('Shared vault'),
     ),
     Str(
-        'user?',
+        'username?',
+        cli_name='user',
         doc=_('Username of the user vault'),
     ),
 )
@@ -234,12 +248,18 @@ class vault(LDAPObject):
         'ipavaulttype',
         'ipavaultsalt',
         'ipavaultpublickey',
+        'owner',
+        'member',
     ]
     search_display_attributes = [
         'cn',
         'description',
         'ipavaulttype',
     ]
+    attribute_members = {
+        'owner': ['user', 'group'],
+        'member': ['user', 'group'],
+    }
 
     label = _('Vaults')
     label_singular = _('Vault')
@@ -282,6 +302,16 @@ class vault(LDAPObject):
             doc=_('Vault public key'),
             flags=['no_search'],
         ),
+        Str(
+            'owner_user?',
+            label=_('Owner users'),
+            flags=['no_create', 'no_update', 'no_search'],
+        ),
+        Str(
+            'owner_group?',
+            label=_('Owner groups'),
+            flags=['no_create', 'no_update', 'no_search'],
+        ),
     )
 
     def get_dn(self, *keys, **options):
@@ -291,7 +321,7 @@ class vault(LDAPObject):
 
         service = options.get('service')
         shared = options.get('shared')
-        user = options.get('user')
+        user = options.get('username')
 
         count = 0
         if service:
@@ -337,7 +367,7 @@ class vault(LDAPObject):
 
         return DN(rdns, parent_dn)
 
-    def create_container(self, dn):
+    def create_container(self, dn, owner_dn):
         """
         Creates vault container and its parents.
         """
@@ -356,6 +386,13 @@ class vault(LDAPObject):
                 {
                     'objectclass': ['nsContainer'],
                     'cn': rdn['cn'],
+                    'aci':
+                        '(targetfilter="(objectClass=ipaVault)")' +
+                        '(version 3.0; ' +
+                        'acl "User can manage private vaults"; ' +
+                        'allow(read, search, compare, add, delete) ' +
+                        'userdn="ldap:///%s";)'
+                        % owner_dn
                 })
 
             # if entry can be added, return
@@ -631,12 +668,21 @@ class vault_add_internal(LDAPCreate):
             raise errors.InvocationError(
                 format=_('KRA service is not enabled'))
 
+        principal = getattr(context, 'principal')
+        (name, realm) = split_principal(principal)
+        if '/' in name:
+            owner_dn = self.api.Object.service.get_dn(name)
+        else:
+            owner_dn = self.api.Object.user.get_dn(name)
+
         try:
             parent_dn = DN(*dn[1:])
-            self.obj.create_container(parent_dn)
+            self.obj.create_container(parent_dn, owner_dn)
         except errors.DuplicateEntry, e:
             pass
 
+        entry_attrs['owner'] = owner_dn
+
         return dn
 
 
@@ -687,6 +733,8 @@ class vault_find(LDAPSearch):
 
     takes_options = LDAPSearch.takes_options + vault_options
 
+    has_output_params = LDAPSearch.has_output_params
+
     msg_summary = ngettext(
         '%(count)d vault matched',
         '%(count)d vaults matched',
@@ -742,6 +790,8 @@ class vault_show(LDAPRetrieve):
 
     takes_options = LDAPRetrieve.takes_options + vault_options
 
+    has_output_params = LDAPRetrieve.has_output_params
+
     def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
 
@@ -1329,6 +1379,68 @@ class vault_retrieve_internal(PKQuery):
 
 
 @register()
+class vault_add_owner(LDAPAddMember):
+    __doc__ = _('Add owners to a vault.')
+
+    takes_options = LDAPAddMember.takes_options + vault_options
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner added.', '%i owners added.')
+
+    has_output = (
+        output.Entry('result'),
+        output.Output(
+            'failed',
+            type=dict,
+            doc=_('Owners that could not be added'),
+        ),
+        output.Output(
+            'completed',
+            type=int,
+            doc=_('Number of owners added'),
+        ),
+    )
+
+
+ at register()
+class vault_remove_owner(LDAPRemoveMember):
+    __doc__ = _('Remove owners from a vault.')
+
+    takes_options = LDAPRemoveMember.takes_options + vault_options
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner removed.', '%i owners removed.')
+
+    has_output = (
+        output.Entry('result'),
+        output.Output(
+            'failed',
+            type=dict,
+            doc=_('Owners that could not be removed'),
+        ),
+        output.Output(
+            'completed',
+            type=int,
+            doc=_('Number of owners removed'),
+        ),
+    )
+
+
+ at register()
+class vault_add_member(LDAPAddMember):
+    __doc__ = _('Add members to a vault.')
+
+    takes_options = LDAPAddMember.takes_options + vault_options
+
+
+ at register()
+class vault_remove_member(LDAPRemoveMember):
+    __doc__ = _('Remove members from a vault.')
+
+    takes_options = LDAPRemoveMember.takes_options + vault_options
+
+
+ at register()
 class kra_is_enabled(Command):
     NO_CLI = True
 
diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py
index 3db93b207fac405ba654b84a2a07668d9a69edb6..fe2f2f67d664e0640fdda99fd3e2f068ee61cb01 100644
--- a/ipatests/test_xmlrpc/test_vault_plugin.py
+++ b/ipatests/test_xmlrpc/test_vault_plugin.py
@@ -89,7 +89,7 @@ class test_vault_plugin(Declarative):
             'continue': True
         }),
         ('vault_del', [vault_name], {'shared': True, 'continue': True}),
-        ('vault_del', [vault_name], {'user': user_name, 'continue': True}),
+        ('vault_del', [vault_name], {'username': user_name, 'continue': True}),
         ('vault_del', [standard_vault_name], {'continue': True}),
         ('vault_del', [symmetric_vault_name], {'continue': True}),
         ('vault_del', [asymmetric_vault_name], {'continue': True}),
@@ -113,6 +113,7 @@ class test_vault_plugin(Declarative):
                     'objectclass': [u'top', u'ipaVault'],
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -154,6 +155,7 @@ class test_vault_plugin(Declarative):
                           % (vault_name, api.env.basedn),
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -174,6 +176,7 @@ class test_vault_plugin(Declarative):
                     'cn': [vault_name],
                     'description': [u'Test vault'],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -212,6 +215,7 @@ class test_vault_plugin(Declarative):
                     'objectclass': [u'top', u'ipaVault'],
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -257,6 +261,7 @@ class test_vault_plugin(Declarative):
                           % (vault_name, service_name, api.env.basedn),
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -278,6 +283,7 @@ class test_vault_plugin(Declarative):
                     'cn': [vault_name],
                     'description': [u'Test vault'],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -318,6 +324,7 @@ class test_vault_plugin(Declarative):
                     'objectclass': [u'top', u'ipaVault'],
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -363,6 +370,7 @@ class test_vault_plugin(Declarative):
                           % (vault_name, api.env.basedn),
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -384,6 +392,7 @@ class test_vault_plugin(Declarative):
                     'cn': [vault_name],
                     'description': [u'Test vault'],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -412,7 +421,7 @@ class test_vault_plugin(Declarative):
                 'vault_add',
                 [vault_name],
                 {
-                    'user': user_name,
+                    'username': user_name,
                 },
             ),
             'expected': {
@@ -424,6 +433,7 @@ class test_vault_plugin(Declarative):
                     'objectclass': [u'top', u'ipaVault'],
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -434,7 +444,7 @@ class test_vault_plugin(Declarative):
                 'vault_find',
                 [],
                 {
-                    'user': user_name,
+                    'username': user_name,
                 },
             ),
             'expected': {
@@ -458,7 +468,7 @@ class test_vault_plugin(Declarative):
                 'vault_show',
                 [vault_name],
                 {
-                    'user': user_name,
+                    'username': user_name,
                 },
             ),
             'expected': {
@@ -469,6 +479,7 @@ class test_vault_plugin(Declarative):
                           % (vault_name, user_name, api.env.basedn),
                     'cn': [vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -479,7 +490,7 @@ class test_vault_plugin(Declarative):
                 'vault_mod',
                 [vault_name],
                 {
-                    'user': user_name,
+                    'username': user_name,
                     'description': u'Test vault',
                 },
             ),
@@ -490,6 +501,7 @@ class test_vault_plugin(Declarative):
                     'cn': [vault_name],
                     'description': [u'Test vault'],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -500,7 +512,7 @@ class test_vault_plugin(Declarative):
                 'vault_del',
                 [vault_name],
                 {
-                    'user': user_name,
+                    'username': user_name,
                 },
             ),
             'expected': {
@@ -528,6 +540,7 @@ class test_vault_plugin(Declarative):
                     'objectclass': [u'top', u'ipaVault'],
                     'cn': [standard_vault_name],
                     'ipavaulttype': [u'standard'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -586,6 +599,7 @@ class test_vault_plugin(Declarative):
                     'cn': [symmetric_vault_name],
                     'ipavaulttype': [u'symmetric'],
                     'ipavaultsalt': [fuzzy_string],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -647,6 +661,7 @@ class test_vault_plugin(Declarative):
                     'cn': [asymmetric_vault_name],
                     'ipavaulttype': [u'asymmetric'],
                     'ipavaultpublickey': [public_key],
+                    'owner_user': [u'admin'],
                 },
             },
         },
-- 
1.9.3



More information about the Freeipa-devel mailing list