[Freeipa-devel] [PATCH] 355 Added vault access control.

Endi Sukma Dewata edewata at redhat.com
Mon Jan 26 19:14:52 UTC 2015


On 11/4/2014 12:28 AM, Endi Sukma Dewata wrote:
> On 10/28/2014 5:35 PM, Endi Sukma Dewata wrote:
>> On 10/22/2014 3:04 PM, Endi Sukma Dewata wrote:
>>> New LDAP ACIs have been added to allow users to create their own
>>> private vault container, to allow owners to manage vaults and
>>> containers, and to allow members to use the vaults. New CLIs have
>>> been added to manage the owner and member list. For archive and
>>> retrieve operations the access control has to be enforced by the
>>> plugins because the operations only affects KRA. The LDAP schema
>>> has been updated as well.
>>>
>>> Ticket #3872
>>>
>>> This patch depends on #353-2.
>>
>> New patch attached to fix the ticket URL. It depends on #353-3.
>
> New patch attached for some cleanups.

Rebased on top of #353-5 to include access control tests.

-- 
Endi S. Dewata
-------------- next part --------------
>From b4591d364a9e38de66536de596347dd08301173e 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 users to create their own
private vault container, to allow owners to manage vaults and
containers, and to allow members to use the vaults. New CLIs have
been added to manage the owner and member list. For archive and
retrieve operations the access control has to be enforced by the
plugins because the operations only affects KRA. The LDAP schema
has been updated as well.

https://fedorahosted.org/freeipa/ticket/3872
---
 API.txt                                   | 134 +++++++++++++++--
 VERSION                                   |   4 +-
 install/share/60basev3.ldif               |   4 +-
 install/updates/40-vault.update           |   7 +
 ipalib/plugins/vault.py                   | 238 +++++++++++++++++++++++++++++-
 ipatests/test_xmlrpc/test_vault_plugin.py |  17 +++
 6 files changed, 389 insertions(+), 15 deletions(-)

diff --git a/API.txt b/API.txt
index 559f4b97fad334f037cb61bbb787f7dfcbd6e23c..f6fd2686a49dfabc053a772818904ca6c14f3b53 100644
--- a/API.txt
+++ b/API.txt
@@ -4518,12 +4518,13 @@ output: Output('result', <type 'bool'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: vault_add
-args: 1,10,3
+args: 1,11,3
 arg: Str('cn', attribute=True, cli_name='vault_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: Bytes('data?', cli_name='data')
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
 option: Str('in?', cli_name='in')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
@@ -4533,13 +4534,40 @@ 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,7,3
+arg: Str('cn', attribute=True, cli_name='vault_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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,7,3
+arg: Str('cn', attribute=True, cli_name='vault_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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,11,3
+args: 1,12,3
 arg: Str('cn', attribute=True, cli_name='vault_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: Bytes('data?', cli_name='data')
 option: Bytes('encrypted_data?', cli_name='encrypted_data')
 option: Str('in?', cli_name='in')
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Bytes('nonce?', cli_name='nonce')
 option: Str('parent?', cli_name='parent')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
@@ -4560,11 +4588,12 @@ output: Output('result', <type 'dict'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfPrimaryKeys('value', None, None)
 command: vault_find
-args: 1,10,4
+args: 1,11,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='vault_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: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False)
 option: Flag('pkey_only?', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
@@ -4577,12 +4606,13 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: vault_mod
-args: 1,10,3
+args: 1,11,3
 arg: Str('cn', attribute=True, cli_name='vault_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')
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
@@ -4592,10 +4622,37 @@ 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,7,3
+arg: Str('cn', attribute=True, cli_name='vault_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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,7,3
+arg: Str('cn', attribute=True, cli_name='vault_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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,9,3
+args: 1,10,3
 arg: Str('cn', attribute=True, cli_name='vault_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: Str('out?', cli_name='out')
 option: Str('parent?', cli_name='parent')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
@@ -4608,9 +4665,10 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: vault_show
-args: 1,5,3
+args: 1,6,3
 arg: Str('cn', attribute=True, cli_name='vault_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: Str('parent?', cli_name='parent')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
@@ -4619,12 +4677,13 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
 command: vaultcontainer_add
-args: 1,8,3
+args: 1,9,3
 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('container_id', attribute=False, cli_name='container_id', multivalue=False, required=False)
 option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
@@ -4632,6 +4691,32 @@ 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: vaultcontainer_add_member
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='container_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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: vaultcontainer_add_owner
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='container_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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: vaultcontainer_del
 args: 1,4,3
 arg: Str('cn', attribute=True, cli_name='container_name', maxlength=255, multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
@@ -4643,12 +4728,13 @@ output: Output('result', <type 'dict'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: ListOfPrimaryKeys('value', None, None)
 command: vaultcontainer_find
-args: 1,10,4
+args: 1,11,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='container_name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False)
 option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, query=True, required=False)
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', query=True, required=False)
 option: Flag('pkey_only?', autofill=True, default=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
@@ -4660,13 +4746,14 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: vaultcontainer_mod
-args: 1,10,3
+args: 1,11,3
 arg: Str('cn', attribute=True, cli_name='container_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')
 option: Str('container_id', attribute=False, autofill=False, cli_name='container_id', multivalue=False, required=False)
 option: Str('delattr*', cli_name='delattr', exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent', attribute=False, autofill=False, cli_name='parent', multivalue=False, pattern='^[a-zA-Z0-9_.-/]+$', required=False)
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Flag('rights', autofill=True, default=False)
@@ -4675,11 +4762,38 @@ 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: vaultcontainer_remove_member
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='container_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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: vaultcontainer_remove_owner
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='container_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: Str('parent?', cli_name='parent')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+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: vaultcontainer_show
-args: 1,5,3
+args: 1,6,3
 arg: Str('cn', attribute=True, cli_name='container_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('continue', autofill=True, cli_name='continue', default=False)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
 option: Str('parent?', cli_name='parent')
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('version?', exclude='webui')
diff --git a/VERSION b/VERSION
index 5acbd7bafee959b97ce2a6584646b5bfb58199d3..b0250045145d17e40df3828d0c50be1db8867625 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=113
-# Last change: edewata - initial vault implementation
+IPA_API_VERSION_MINOR=114
+# Last change: edewata - added vault access control
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index e0bc4151d88fc394cea0d81a20d8da9537dd5cd3..4883132f1627e16a16b6e7effed2af4be287e251 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -77,5 +77,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 ) 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 ) 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 $ 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 $ member ) X-ORIGIN 'IPA v4.2' )
diff --git a/install/updates/40-vault.update b/install/updates/40-vault.update
index dac2f67112dc33f012c6d559285464fb7c944d1a..4f36a8066504fb3577fc9f78c830412e9702f617 100644
--- a/install/updates/40-vault.update
+++ b/install/updates/40-vault.update
@@ -3,6 +3,13 @@ default: objectClass: top
 default: objectClass: ipaVaultContainer
 default: cn: vaults
 default: description: Root vault container
+default: aci: (target="ldap:///cn=*,cn=users,cn=vaults,$SUFFIX")(targetattr="*")(version 3.0; acl "Allow add macro dn"; allow (add) userdn = "ldap:///uid=($$attr.cn),cn=users,cn=accounts,$SUFFIX";)
+default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container members can access the container"; allow(read, search, compare) userattr="member#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can modify the container"; allow(read, search, compare, write) userattr="owner#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVaultContainer)")(targetattr="*")(version 3.0; acl "Container owners can manage sub-containers"; allow(read, search, compare, add, delete) userattr="parent[1].owner#USERDN";)
+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 "Vault members can access the vault"; allow(read, search, compare) userattr="member#USERDN";)
+default: aci: (targetfilter="(objectClass=ipaVault)")(targetattr="*")(version 3.0; acl "Vault owners can modify the vault"; allow(read, search, compare, write) userattr="owner#USERDN";)
 
 dn: cn=services,cn=vaults,$SUFFIX
 default: objectClass: top
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 25372d0f02058fb5efd9f5dece96073ea1d78646..7ec36836a741bc0669ab1be59192532136e8e126 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -34,7 +34,8 @@ from ipalib import api, errors
 from ipalib import Str, Bytes, Flag
 from ipalib.frontend import Command
 from ipalib.plugable import Registry
-from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve
+from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete, LDAPSearch, LDAPUpdate, LDAPRetrieve,\
+                                    LDAPAddMember, LDAPRemoveMember
 from ipalib.request import context
 from ipalib.plugins.user import split_principal
 from ipalib import _, ngettext
@@ -73,6 +74,18 @@ EXAMPLES:
  Delete a vault:
    ipa vault-del MyVault
 """) + _("""
+ Add a vault owner:
+   ipa vault-add-owner MyVault --users testuser
+""") + _("""
+ Delete a vault owner:
+   ipa vault-remove-owner MyVault --users testuser
+""") + _("""
+ Add a vault member:
+   ipa vault-add-member MyVault --users testuser
+""") + _("""
+ Delete a vault member:
+   ipa vault-remove-member MyVault --users testuser
+""") + _("""
  List private vault containers:
    ipa vaultcontainer-find
 """) + _("""
@@ -90,6 +103,18 @@ EXAMPLES:
 """) + _("""
  Delete a vault container:
    ipa vaultcontainer-del MyContainer
+""") + _("""
+ Add a vault container owner:
+   ipa vaultcontainer-add-owner MyContainer --users testuser
+""") + _("""
+ Delete a vault container owner:
+   ipa vaultcontainer-remove-owner MyContainer --users testuser
+""") + _("""
+ Add a vault container member:
+   ipa vaultcontainer-add-member MyContainer --users testuser
+""") + _("""
+ Delete a vault container member:
+   ipa vaultcontainer-remove-member MyContainer --users testuser
 """)
 
 register = Registry()
@@ -110,12 +135,18 @@ class vaultcontainer(LDAPObject):
         'cn',
         'container_id',
         'description',
+        'owner',
+        'member',
     ]
     search_display_attributes = [
         'cn',
         'container_id',
         'description',
     ]
+    attribute_members = {
+        'owner': ['user', 'group'],
+        'member': ['user', 'group'],
+    }
 
     label = _('Vault Containers')
     label_singular = _('Vault Container')
@@ -252,6 +283,11 @@ class vaultcontainer_add(LDAPCreate):
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
 
+        principal = getattr(context, 'principal')
+        (username, realm) = split_principal(principal)
+        owner_dn = self.api.Object['user'].get_dn(username)
+        entry_attrs['owner'] = owner_dn
+
         # add parent containers if they don't exist
         try:
             ldap = self.obj.backend
@@ -265,6 +301,7 @@ class vaultcontainer_add(LDAPCreate):
                     {
                         'objectclass': api.Object.vaultcontainer.object_class,
                         'cn': parent_rdn['cn'],
+                        'owner': owner_dn,
                     })
 
                 ldap.add_entry(parent)
@@ -389,6 +426,89 @@ class vaultcontainer_show(LDAPRetrieve):
 
 
 @register()
+class vaultcontainer_add_owner(LDAPAddMember):
+    __doc__ = _('Add owners to a vault container.')
+
+    takes_options = LDAPAddMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner added.', '%i owners added.')
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['container_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vaultcontainer_remove_owner(LDAPRemoveMember):
+    __doc__ = _('Remove owners from a vault container.')
+
+    takes_options = LDAPRemoveMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner removed.', '%i owners removed.')
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['container_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vaultcontainer_add_member(LDAPAddMember):
+    __doc__ = _('Add members to a vault container.')
+
+    takes_options = LDAPAddMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['container_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vaultcontainer_remove_member(LDAPRemoveMember):
+    __doc__ = _('Remove members from a vault container.')
+
+
+    takes_options = LDAPRemoveMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['container_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
 class vault(LDAPObject):
     __doc__ = _("""
     Vault object.
@@ -402,12 +522,18 @@ class vault(LDAPObject):
         'cn',
         'vault_id',
         'description',
+        'owner',
+        'member',
     ]
     search_display_attributes = [
         'cn',
         'vault_id',
         'description',
     ]
+    attribute_members = {
+        'owner': ['user', 'group'],
+        'member': ['user', 'group'],
+    }
 
     label = _('Vaults')
     label_singular = _('Vault')
@@ -586,6 +712,11 @@ class vault_add(LDAPCreate):
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
 
+        principal = getattr(context, 'principal')
+        (username, realm) = split_principal(principal)
+        owner_dn = self.api.Object['user'].get_dn(username)
+        entry_attrs['owner'] = owner_dn
+
         # add parent containers if they don't exist
         try:
             ldap = self.obj.backend
@@ -599,6 +730,7 @@ class vault_add(LDAPCreate):
                     {
                         'objectclass': api.Object.vaultcontainer.object_class,
                         'cn': parent_rdn['cn'],
+                        'owner': owner_dn,
                     })
 
                 ldap.add_entry(parent)
@@ -826,6 +958,17 @@ class vault_archive(LDAPRetrieve):
         assert isinstance(dn, DN)
 
         vault_id = self.obj.get_id(dn)
+
+        owners = entry_attrs.get('owner', [])
+        members = entry_attrs.get('member', [])
+
+        principal = getattr(context, 'principal')
+        (username, realm) = split_principal(principal)
+        user_dn = self.api.Object['user'].get_dn(username)
+
+        if user_dn not in owners and user_dn not in members:
+            raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id)
+
         entry_attrs['vault_id'] = vault_id
 
         # connect to KRA
@@ -970,6 +1113,17 @@ class vault_retrieve(LDAPRetrieve):
         assert isinstance(dn, DN)
 
         vault_id = self.obj.get_id(dn)
+
+        owners = entry_attrs.get('owner', [])
+        members = entry_attrs.get('member', [])
+
+        principal = getattr(context, 'principal')
+        (username, realm) = split_principal(principal)
+        user_dn = self.api.Object['user'].get_dn(username)
+
+        if user_dn not in owners and user_dn not in members:
+            raise errors.ACIError(info=_("Insufficient access to vault '%s'.") % vault_id)
+
         entry_attrs['vault_id'] = vault_id
 
         wrapped_session_key = base64.b64decode(options['wrapped_session_key'])
@@ -1001,3 +1155,85 @@ class vault_retrieve(LDAPRetrieve):
         kra_account.logout()
 
         return dn
+
+
+ at register()
+class vault_add_owner(LDAPAddMember):
+    __doc__ = _('Add owners to a vault.')
+
+    takes_options = LDAPAddMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner added.', '%i owners added.')
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['vault_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vault_remove_owner(LDAPRemoveMember):
+    __doc__ = _('Remove owners from a vault.')
+
+    takes_options = LDAPRemoveMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    member_attributes = ['owner']
+    member_count_out = ('%i owner removed.', '%i owners removed.')
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['vault_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vault_add_member(LDAPAddMember):
+    __doc__ = _('Add members to a vault.')
+
+    takes_options = LDAPAddMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['vault_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
+
+
+ at register()
+class vault_remove_member(LDAPRemoveMember):
+    __doc__ = _('Remove members from a vault.')
+
+    takes_options = LDAPRemoveMember.takes_options + (
+        Str('parent?',
+            cli_name='parent',
+            doc=_('Parent container'),
+        ),
+    )
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        assert isinstance(dn, DN)
+
+        entry_attrs['vault_id'] = self.obj.get_id(dn)
+
+        return (completed, dn)
diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py
index 01896ce1811dd8b3829bc266ff288494fdb3746f..9ef30c1035880dba839922570bf568bcdc6d0937 100644
--- a/ipatests/test_xmlrpc/test_vault_plugin.py
+++ b/ipatests/test_xmlrpc/test_vault_plugin.py
@@ -97,6 +97,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVaultContainer', u'top'),
                     'cn': [private_container],
                     'container_id': u'/users/admin/%s/' % private_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -146,6 +147,7 @@ class test_vault(Declarative):
                     'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_container, api.env.basedn),
                     'cn': [private_container],
                     'container_id': u'/users/admin/%s/' % private_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -166,6 +168,7 @@ class test_vault(Declarative):
                     'cn': [private_container],
                     'container_id': u'/users/admin/%s/' % private_container,
                     'description': [u'Private container'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -213,6 +216,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVaultContainer', u'top'),
                     'cn': [shared_container],
                     'container_id': u'/shared/%s/' % shared_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -256,6 +260,7 @@ class test_vault(Declarative):
                     'dn': u'cn=%s,cn=shared,cn=vaults,%s' % (shared_container, api.env.basedn),
                     'cn': [shared_container],
                     'container_id': u'/shared/%s/' % shared_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -277,6 +282,7 @@ class test_vault(Declarative):
                     'cn': [shared_container],
                     'container_id': u'/shared/%s/' % shared_container,
                     'description': [u'shared container'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -316,6 +322,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVaultContainer', u'top'),
                     'cn': [service_container],
                     'container_id': u'/services/%s/' % service_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -334,6 +341,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVaultContainer', u'top'),
                     'cn': [base_container],
                     'container_id': u'/users/admin/%s/' % base_container,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -355,6 +363,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVaultContainer', u'top'),
                     'cn': [child_container],
                     'container_id': u'/users/admin/%s/%s/' % (base_container, child_container),
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -378,6 +387,7 @@ class test_vault(Declarative):
                     'cn': [grandchild_container],
                     'container_id': u'/users/admin/%s/%s/%s/'
                         % (base_container, child_container, grandchild_container),
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -425,6 +435,7 @@ class test_vault(Declarative):
                     'objectclass': (u'ipaVault', u'top'),
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -474,6 +485,7 @@ class test_vault(Declarative):
                     'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s' % (private_vault, api.env.basedn),
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -494,6 +506,7 @@ class test_vault(Declarative):
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
                     'description': [u'Private vault'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -515,6 +528,7 @@ class test_vault(Declarative):
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
                     'description': [u'Private vault'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -534,6 +548,7 @@ class test_vault(Declarative):
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
                     'description': [u'Private vault'],
+                    'owner_user': [u'admin'],
                     'nonce': fuzzy_string,
                     'encrypted_data': fuzzy_string,
                     'data': 'c2JVcmV0',
@@ -558,6 +573,7 @@ class test_vault(Declarative):
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
                     'description': [u'Private vault'],
+                    'owner_user': [u'admin'],
                 },
             },
         },
@@ -579,6 +595,7 @@ class test_vault(Declarative):
                     'cn': [private_vault],
                     'vault_id': u'/users/admin/%s' % private_vault,
                     'description': [u'Private vault'],
+                    'owner_user': [u'admin'],
                     'nonce': fuzzy_string,
                     'encrypted_data': fuzzy_string,
                     'text': u'secret',
-- 
1.9.0



More information about the Freeipa-devel mailing list