----- Original Message -----
> On 07/07/2015 10:51 AM, Jan Cholasta wrote:
> > Dne 3.7.2015 v 15:44 Endi Sukma Dewata napsal(a):
> >> Here is the rebased patch for vault access control.
> >>
> > 
> > LGTM, except:
> > 
> > @@ -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
> > 
> > I don't think dynamically creating ACIs with hardcoded userdn is something
> > we
> > want to do. This should be handled by a single ACI in cn=vaults.
> 
> +1. Single ACI like
> 
> +default: aci: (targetfilter="(objectClass=ipaVault)")(version 3.0; acl
> "Vault
> owners can manage the vault"; allow(read, search, compare, write)
> userattr="owner#USERDN";)
> 
> you already have there is more preferred.

New patch attached. For this to work the container itself needs an 'owner' 
attribute, so I changed the nsContainer into ipaVaultContainer.

--
Endi S. Dewata
From 087f36b888e068ee732af6e0a2c24b1d50849ccd Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edew...@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               |   3 +-
 install/share/vault.update                |  15 +++-
 ipalib/plugins/vault.py                   | 118 ++++++++++++++++++++++++++++--
 ipatests/test_xmlrpc/test_vault_plugin.py |  27 +++++--
 6 files changed, 226 insertions(+), 33 deletions(-)

diff --git a/API.txt b/API.txt
index 99fa528733200fc3d797a9847b1d6df2188b92d5..98eaee8ce8b2804a6d34e42c3eff26ddb3851963 100644
--- a/API.txt
+++ b/API.txt
@@ -5422,27 +5422,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)
@@ -5454,7 +5485,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)
@@ -5468,7 +5499,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))
@@ -5480,32 +5511,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')
@@ -5514,16 +5546,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)
@@ -5536,7 +5599,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)
@@ -5549,20 +5612,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 2d9ad2697ab8c998ce29cc66a8b2776687e3712a..79ff515f556552c8b3e821095993bb6a28c3ff1a 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=139
-# Last change: edewata - added ipaVaultPublicKey attribute
+IPA_API_VERSION_MINOR=140
+# Last change: edewata - added vault access control
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 5491f99f5e78f122f94e9215bf5751d487f19d2e..16d7c21d9291d1cd1bdc70f7d908191a7939dd56 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -82,4 +82,5 @@ 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' )
+objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner ) X-ORIGIN 'IPA v4.2' )
diff --git a/install/share/vault.update b/install/share/vault.update
index dcd1e2a152b82263f0a2653eb5604a3c76002dd9..61a8940b544fbc839b931f337389ac35dc2d1ffa 100644
--- a/install/share/vault.update
+++ b/install/share/vault.update
@@ -5,20 +5,27 @@ default: cn: kra
 
 dn: cn=vaults,cn=kra,$SUFFIX
 default: objectClass: top
-default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
 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)")(targetattr="*")(version 3.0; acl "Container owners can manage vaults in the container"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Indirect container owners can manage vaults in the container"; allow(read, search, compare, add, delete) userattr="parent[1].owner#GROUPDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault members can access the vault"; allow(read, search, compare) userattr="member#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Indirect vault members can access the vault"; allow(read, search, compare) userattr="member#GROUPDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault owners can manage the vault"; allow(read, search, compare, write) userattr="owner#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(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
-default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
 default: cn: services
 
 dn: cn=shared,cn=vaults,cn=kra,$SUFFIX
 default: objectClass: top
-default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
 default: cn: shared
 
 dn: cn=users,cn=vaults,cn=kra,$SUFFIX
 default: objectClass: top
-default: objectClass: nsContainer
+default: objectClass: ipaVaultContainer
 default: cn: users
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 9fcd619d19de9ae036a73bb3af9dc050c6be6c76..37a32282e7a4e87889eea90d987b737f98fd82c3 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.
         """
@@ -354,8 +384,9 @@ class vault(LDAPObject):
             entry = self.backend.make_entry(
                 dn,
                 {
-                    'objectclass': ['nsContainer'],
+                    'objectclass': ['ipaVaultContainer'],
                     'cn': rdn['cn'],
+                    'owner': [owner_dn],
                 })
 
             # if entry can be added, return
@@ -631,12 +662,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 +727,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 +784,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 +1373,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'),
+        ),
+    )
+
+
+@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'),
+        ),
+    )
+
+
+@register()
+class vault_add_member(LDAPAddMember):
+    __doc__ = _('Add members to a vault.')
+
+    takes_options = LDAPAddMember.takes_options + vault_options
+
+
+@register()
+class vault_remove_member(LDAPRemoveMember):
+    __doc__ = _('Remove members from a vault.')
+
+    takes_options = LDAPRemoveMember.takes_options + vault_options
+
+
+@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

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to