Please take a look at the updated patch.

On 5/27/2015 12:39 AM, Jan Cholasta wrote:
21) vault_archive is not a retrieve operation, it should be based on
LDAPUpdate instead of LDAPRetrieve. Or Command actually, since it
does
not do anything with LDAP. The same applies to vault_retrieve.

The vault_archive does not actually modify the LDAP entry because it
stores the data in KRA. It is actually an LDAPRetrieve operation
because
it needs to get the vault info before it can perform the archival
operation. Same thing with vault_retrieve.

It is not a LDAPRetrieve operation, because it has different
semantics.
Please use Command as base class and either use ldap2 for direct
LDAP or
call vault_show instead of hacking around LDAPRetrieve.

It's been changed to inherit from LDAPQuery instead.

NACK, it's not a LDAPQuery operation, because it has different
semantics. There is more to a command than executing code, so you should
use a correct base class.

Changed to inherit from Command as requested. Now these commands no
longer have a direct access to the vault object (self.obj) although they
are accessing vault objects like other vault commands. Also now the
vault name argument has to be added explicitly on each command.

You can inherit from crud.Retrieve and crud.Update to get self.obj and
the argument back.

I tried this:

  class vault_retrieve(Command, crud.Retrieve):

and it gave me an error:

  TypeError: Error when calling the metaclass bases
      Cannot create a consistent method resolution
  order (MRO) for bases Retrieve, Command

I'm sticking with the original code since it works fine although not ideal. I'm not a Python expert, so if you know how to fix this properly please feel free to post a patch on top of this.

If KRA is not installed, vault-archive and vault-retrieve fail with
internal error.

Added a code to check KRA installation in all vault commands. If you know a way not to load the vault plugin if the KRA is not installed please let me know, that's probably even better. Not sure how that will work on the client side though.

The commands still behave differently based on whether they were called
from API which was initialized with in_server set to True or False.

That is unfortunately a restriction imposed by the framework. In order to guarantee the security, the vault is designed to have separate client and server code. The client code encrypts the secret, the server code forwards the encrypted secret to KRA. To archive a secret into a vault properly, you are supposed to call the client code. If you're calling the server code directly, you are responsible to do your own encryption (i.e. generating session key, nonce, and vault data).

If another plugin wants to use vault, it should implement a client code which calls the vault client code to perform the archival from the client side.

What is the use case for calling the vault API from the server side anyway? Wouldn't that defeat the purpose of having a vault? If a secret exists on the server side in an unencrypted form doesn't it mean the secret may already have been compromised?

There is no point in exposing the session_key, nonce and vault_data
options in CLI when their value is always overwritten in forward().

I agree there is no need to expose them in CLI, but in this framework the API also defines the CLI. If there's a way to keep them in the server API but not expose them in the CLI please let me know. Or, if there's a way to define completely separate server API (without a matching client CLI) and client CLI (without a matching server API) that will work too.

Will this always succeed?

+        # deactivate vault record in KRA
+        response = kra_client.keys.list_keys(
+            client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE)

Yes. If there's no active keys it will return an empty collection.

+        for key_info in response.key_infos:
+            kra_client.keys.modify_key_status(
+                key_info.get_key_id(),
+                pki.key.KeyClient.KEY_STATUS_INACTIVE)

This loop will do nothing given an empty collection.

If not, we might get into an inconsistent state, where the vault is
deleted in LDAP but still active in KRA. (I'm not sure if this is
actually a problem or not.)

That can only happen if the server crashes after deleting the vault but before deactivating the key. Regardless, it will not be a problem because the key is identified by vault ID/path so it will not conflict with other vaults, and it will get overwritten if the same vault is recreated again.

--
Endi S. Dewata
>From d1123f07745fea856ced305a814d933cd793dbf2 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edew...@redhat.com>
Date: Tue, 21 Oct 2014 10:57:08 -0400
Subject: [PATCH] Added vault-archive and vault-retrieve commands.

New commands have been added to archive and retrieve
data into and from a vault, also to retrieve the
transport certificate.

https://fedorahosted.org/freeipa/ticket/3872
---
 API.txt                                   |  28 ++
 VERSION                                   |   4 +-
 ipalib/plugins/vault.py                   | 501 +++++++++++++++++++++++++++++-
 ipatests/test_xmlrpc/test_vault_plugin.py |  71 ++++-
 4 files changed, 600 insertions(+), 4 deletions(-)

diff --git a/API.txt b/API.txt
index 
da69f32de5c12c0d85a7d61d9027385aa3c0ee05..3741e6f16689e43838c2d31a44872d1ea47589c7
 100644
--- a/API.txt
+++ b/API.txt
@@ -4768,6 +4768,24 @@ 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_archive
+args: 1,9,1
+arg: Str('cn', cli_name='name', maxlength=255, pattern='^[a-zA-Z0-9_.-]+$')
+option: Bytes('data?')
+option: Str('in?')
+option: Str('nonce?')
+option: Str('service?')
+option: Str('session_key?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user?')
+option: Str('vault_data?')
+option: Str('version?', exclude='webui')
+output: Output('result', None, None)
+command: vault_config
+args: 0,2,1
+option: Str('transport_out?')
+option: Str('version?', exclude='webui')
+output: Output('result', None, None)
 command: vault_del
 args: 1,5,3
 arg: Str('cn', attribute=True, cli_name='name', maxlength=255, 
multivalue=True, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, 
required=True)
@@ -4814,6 +4832,16 @@ 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_retrieve
+args: 1,6,1
+arg: Str('cn', cli_name='name', maxlength=255, pattern='^[a-zA-Z0-9_.-]+$')
+option: Str('out?')
+option: Str('service?')
+option: Str('session_key?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user?')
+option: Str('version?', exclude='webui')
+output: Output('result', None, None)
 command: vault_show
 args: 1,7,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)
diff --git a/VERSION b/VERSION
index 
07c00d000064a7687497b09524aa821dbcecc88a..2bfb2fe46b3760f30e1aa378841544a51f014728
 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=121
-# Last change: pvoborni - added server-find and server-show
+IPA_API_VERSION_MINOR=122
+# Last change: edewata - added vault-archive and vault-retrieve
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 
ebb9f9fd3cf3b5a7d6b44ac9e63e122e8f71aa1a..cb68d136e98efa73ce8281ee12ef897af67c3da9
 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -17,8 +17,21 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import base64
+import json
+import os
+import sys
+import tempfile
+
+import nss.nss as nss
+
+import pki.account
+import pki.crypto
+import pki.key
+
+from ipalib.frontend import Command
 from ipalib import api, errors
-from ipalib import Str, Flag
+from ipalib import Bytes, Str, Flag
 from ipalib import output
 from ipalib.plugable import Registry
 from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, LDAPDelete,\
@@ -26,7 +39,9 @@ from ipalib.plugins.baseldap import LDAPObject, LDAPCreate, 
LDAPDelete,\
 from ipalib.request import context
 from ipalib.plugins.user import split_principal
 from ipalib import _, ngettext
+from ipaplatform.paths import paths
 from ipapython.dn import DN
+from ipapython.nsslib import current_dbdir
 
 __doc__ = _("""
 Vaults
@@ -94,6 +109,33 @@ EXAMPLES:
 """) + _("""
  Delete a user vault:
    ipa vault-del <name> --user <username>
+""") + _("""
+ Display vault configuration:
+   ipa vault-config
+""") + _("""
+ Archive data into private vault:
+   ipa vault-archive <name> --in <input file>
+""") + _("""
+ Archive data into service vault:
+   ipa vault-archive <name> --service <service name> --in <input file>
+""") + _("""
+ Archive data into shared vault:
+   ipa vault-archive <name> --shared --in <input file>
+""") + _("""
+ Archive data into user vault:
+   ipa vault-archive <name> --user <username> --in <input file>
+""") + _("""
+ Retrieve data from private vault:
+   ipa vault-retrieve <name> --out <output file>
+""") + _("""
+ Retrieve data from service vault:
+   ipa vault-retrieve <name> --service <service name> --out <output file>
+""") + _("""
+ Retrieve data from shared vault:
+   ipa vault-retrieve <name> --shared --out <output file>
+""") + _("""
+ Retrieve data from user vault:
+   ipa vault-retrieve <name> --user <user name> --out <output file>
 """)
 
 register = Registry()
@@ -243,6 +285,26 @@ class vault(LDAPObject):
         for entry in entries:
             self.backend.add_entry(entry)
 
+    def get_key_id(self, dn):
+        """
+        Generates a client key ID to archive/retrieve data in KRA.
+        """
+
+        # TODO: create container_dn after object initialization then reuse it
+        container_dn = DN(self.container_dn, self.api.env.basedn)
+
+        # make sure the DN is a vault DN
+        if not dn.endswith(container_dn, 1):
+            raise ValueError('Invalid vault DN: %s' % dn)
+
+        # construct the vault ID from the bottom up
+        id = u''
+        for rdn in dn[:-len(container_dn)]:
+            name = rdn['cn']
+            id = u'/' + name + id
+
+        return 'ipa:' + id
+
 
 @register()
 class vault_add(LDAPCreate):
@@ -256,6 +318,10 @@ class vault_add(LDAPCreate):
                      **options):
         assert isinstance(dn, DN)
 
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
         try:
             parent_dn = DN(*dn[1:])
             self.obj.create_container(parent_dn)
@@ -273,6 +339,38 @@ class vault_del(LDAPDelete):
 
     msg_summary = _('Deleted vault "%(value)s"')
 
+    def pre_callback(self, ldap, dn, *keys, **options):
+        assert isinstance(dn, DN)
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        return dn
+
+    def post_callback(self, ldap, dn, *args, **options):
+        assert isinstance(dn, DN)
+
+        kra_client = self.api.Backend.kra.get_client()
+
+        kra_account = pki.account.AccountClient(kra_client.connection)
+        kra_account.login()
+
+        client_key_id = self.obj.get_key_id(dn)
+
+        # deactivate vault record in KRA
+        response = kra_client.keys.list_keys(
+            client_key_id, pki.key.KeyClient.KEY_STATUS_ACTIVE)
+
+        for key_info in response.key_infos:
+            kra_client.keys.modify_key_status(
+                key_info.get_key_id(),
+                pki.key.KeyClient.KEY_STATUS_INACTIVE)
+
+        kra_account.logout()
+
+        return True
+
 
 @register()
 class vault_find(LDAPSearch):
@@ -290,6 +388,10 @@ class vault_find(LDAPSearch):
                      **options):
         assert isinstance(base_dn, DN)
 
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
         base_dn = self.obj.get_dn(*args, **options)
 
         return (filter, base_dn, scope)
@@ -313,9 +415,406 @@ class vault_mod(LDAPUpdate):
 
     msg_summary = _('Modified vault "%(value)s"')
 
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list,
+                     *keys, **options):
+
+        assert isinstance(dn, DN)
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        return dn
+
 
 @register()
 class vault_show(LDAPRetrieve):
     __doc__ = _('Display information about a vault.')
 
     takes_options = LDAPRetrieve.takes_options + vault_options
+
+    def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+        assert isinstance(dn, DN)
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        return dn
+
+
+@register()
+class vault_config(Command):
+    __doc__ = _('Show vault configuration.')
+
+    takes_options = (
+        Str(
+            'transport_out?',
+            doc=_('Output file to store the transport certificate'),
+        ),
+    )
+
+    has_output_params = (
+        Str(
+            'transport_cert',
+            label=_('Transport Certificate'),
+        ),
+    )
+
+    def forward(self, *args, **options):
+
+        file = options.get('transport_out')
+
+        # don't send these parameters to server
+        if 'transport_out' in options:
+            del options['transport_out']
+
+        response = super(vault_config, self).forward(*args, **options)
+
+        if file:
+            with open(file, 'w') as f:
+                f.write(response['result']['transport_cert'])
+
+        return response
+
+    def execute(self, *args, **options):
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        kra_client = self.api.Backend.kra.get_client()
+        transport_cert = kra_client.system_certs.get_transport_cert()
+        return {
+            'result': {
+                'transport_cert': transport_cert.encoded
+            }
+        }
+
+
+@register()
+class vault_archive(Command):
+    __doc__ = _('Archive data into a vault.')
+
+    takes_args = (
+        Str(
+            'cn',
+            cli_name='name',
+            label=_('Vault name'),
+            pattern='^[a-zA-Z0-9_.-]+$',
+            pattern_errmsg='may only include letters, numbers, _, ., and -',
+            maxlength=255,
+        ),
+    )
+
+    takes_options = vault_options + (
+        Bytes(
+            'data?',
+            doc=_('Binary data to archive'),
+        ),
+        Str(  # TODO: use File parameter
+            'in?',
+            doc=_('File containing data to archive'),
+        ),
+        Str(
+            'session_key?',
+            doc=_(
+                'Session key wrapped with transport certificate'
+                ' and encoded in base-64'),
+        ),
+        Str(
+            'vault_data?',
+            doc=_(
+                'Vault data encrypted with session key'
+                ' and encoded in base-64'),
+        ),
+        Str(
+            'nonce?',
+            doc=_('Nonce encrypted encoded in base-64'),
+        ),
+    )
+
+    msg_summary = _('Archived data into vault "%(value)s"')
+
+    def forward(self, *args, **options):
+
+        data = options.get('data')
+        input_file = options.get('in')
+
+        # don't send these parameters to server
+        if 'data' in options:
+            del options['data']
+        if 'in' in options:
+            del options['in']
+
+        # get data
+        if data and input_file:
+            raise errors.MutuallyExclusiveError(
+                reason=_('Input data specified multiple times'))
+
+        if input_file:
+            with open(input_file, 'rb') as f:
+                data = f.read()
+
+        elif not data:
+            data = ''
+
+        # initialize NSS database
+        crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR)
+        crypto.initialize()
+        current_dbdir = paths.IPA_NSSDB_DIR
+
+        # retrieve transport certificate
+        (file, filename) = tempfile.mkstemp()
+        os.close(file)
+        try:
+            self.api.Command.vault_config(transport_out=unicode(filename))
+            transport_cert_der = nss.read_der_from_file(filename, True)
+            nss_transport_cert = nss.Certificate(transport_cert_der)
+
+        finally:
+            os.remove(filename)
+
+        # generate session key
+        session_key = crypto.generate_session_key()
+
+        # wrap session key with transport certificate
+        wrapped_session_key = crypto.asymmetric_wrap(
+            session_key,
+            nss_transport_cert
+        )
+
+        options['session_key'] = base64.b64encode(wrapped_session_key)\
+            .decode('utf-8')
+
+        nonce = crypto.generate_nonce_iv()
+        options['nonce'] = base64.b64encode(nonce).decode('utf-8')
+
+        vault_data = {}
+        vault_data[u'data'] = base64.b64encode(data).decode('utf-8')
+
+        json_vault_data = json.dumps(vault_data)
+
+        # wrap vault_data with session key
+        wrapped_vault_data = crypto.symmetric_wrap(
+            json_vault_data,
+            session_key,
+            nonce_iv=nonce
+        )
+
+        options['vault_data'] = base64.b64encode(wrapped_vault_data)\
+            .decode('utf-8')
+
+        return super(vault_archive, self).forward(*args, **options)
+
+    def execute(self, *args, **options):
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        vault_name = args[0]
+
+        # retrieve vault info
+        vault = self.api.Command.vault_show(
+            vault_name,
+            service=options.get('service'),
+            shared=options.get('shared'),
+            user=options.get('user'),
+        )['result']
+
+        # connect to KRA
+        kra_client = self.api.Backend.kra.get_client()
+
+        kra_account = pki.account.AccountClient(kra_client.connection)
+        kra_account.login()
+
+        client_key_id = self.api.Object.vault.get_key_id(vault['dn'])
+
+        # deactivate existing vault record in KRA
+        response = kra_client.keys.list_keys(
+            client_key_id,
+            pki.key.KeyClient.KEY_STATUS_ACTIVE)
+
+        for key_info in response.key_infos:
+            kra_client.keys.modify_key_status(
+                key_info.get_key_id(),
+                pki.key.KeyClient.KEY_STATUS_INACTIVE)
+
+        wrapped_session_key = base64.b64decode(options['session_key'])
+        nonce = base64.b64decode(options['nonce'])
+
+        # forward wrapped data to KRA
+        wrapped_vault_data = base64.b64decode(options['vault_data'])
+
+        kra_client.keys.archive_encrypted_data(
+            client_key_id,
+            pki.key.KeyClient.PASS_PHRASE_TYPE,
+            wrapped_vault_data,
+            wrapped_session_key,
+            None,
+            nonce,
+        )
+
+        kra_account.logout()
+
+        response = {}
+        response['result'] = {}
+
+        return response
+
+
+@register()
+class vault_retrieve(Command):
+    __doc__ = _('Retrieve a data from a vault.')
+
+    takes_args = (
+        Str(
+            'cn',
+            cli_name='name',
+            label=_('Vault name'),
+            pattern='^[a-zA-Z0-9_.-]+$',
+            pattern_errmsg='may only include letters, numbers, _, ., and -',
+            maxlength=255,
+        ),
+    )
+
+    takes_options = vault_options + (
+        Str(
+            'out?',
+            doc=_('File to store retrieved data'),
+        ),
+        Str(
+            'session_key?',
+            doc=_(
+                'Session key wrapped with transport certificate'
+                ' and encoded in base-64'),
+        ),
+    )
+
+    has_output_params = (
+        Bytes(
+            'data',
+            label=_('Data'),
+        ),
+    )
+
+    msg_summary = _('Retrieved data from vault "%(value)s"')
+
+    def forward(self, *args, **options):
+
+        output_file = options.get('out')
+
+        # don't send these parameters to server
+        if 'out' in options:
+            del options['out']
+
+        # initialize NSS database
+        crypto = pki.crypto.NSSCryptoProvider(paths.IPA_NSSDB_DIR)
+        crypto.initialize()
+        current_dbdir = paths.IPA_NSSDB_DIR
+
+        # retrieve transport certificate
+        (file, filename) = tempfile.mkstemp()
+        os.close(file)
+        try:
+            self.api.Command.vault_config(transport_out=unicode(filename))
+            transport_cert_der = nss.read_der_from_file(filename, True)
+            nss_transport_cert = nss.Certificate(transport_cert_der)
+
+        finally:
+            os.remove(filename)
+
+        # generate session key
+        session_key = crypto.generate_session_key()
+
+        # wrap session key with transport certificate
+        wrapped_session_key = crypto.asymmetric_wrap(
+            session_key,
+            nss_transport_cert
+        )
+
+        # send retrieval request to server
+        options['session_key'] = base64.b64encode(wrapped_session_key)\
+            .decode('utf-8')
+
+        response = super(vault_retrieve, self).forward(*args, **options)
+
+        result = response['result']
+        nonce = base64.b64decode(result['nonce'])
+
+        # unwrap data with session key
+        wrapped_vault_data = base64.b64decode(result['vault_data'])
+
+        json_vault_data = crypto.symmetric_unwrap(
+            wrapped_vault_data,
+            session_key,
+            nonce_iv=nonce)
+
+        vault_data = json.loads(json_vault_data)
+        data = base64.b64decode(vault_data[u'data'].encode('utf-8'))
+
+        if output_file:
+            response = {}
+            response['result'] = {}
+            with open(output_file, 'w') as f:
+                f.write(data)
+
+        else:
+            response['result']['data'] = data
+            del response['result']['nonce']
+            del response['result']['vault_data']
+
+        return response
+
+    def execute(self, *args, **options):
+
+        if not self.api.env.enable_kra:
+            raise errors.InvocationError(
+                format=_('KRA service is not enabled'))
+
+        vault_name = args[0]
+
+        # retrieve vault info
+        vault = self.api.Command.vault_show(
+            vault_name,
+            service=options.get('service'),
+            shared=options.get('shared'),
+            user=options.get('user'),
+        )['result']
+
+        wrapped_session_key = base64.b64decode(options['session_key'])
+
+        # connect to KRA
+        kra_client = self.api.Backend.kra.get_client()
+
+        kra_account = pki.account.AccountClient(kra_client.connection)
+        kra_account.login()
+
+        client_key_id = self.api.Object.vault.get_key_id(vault['dn'])
+
+        # find vault record in KRA
+        response = kra_client.keys.list_keys(
+            client_key_id,
+            pki.key.KeyClient.KEY_STATUS_ACTIVE)
+
+        if not len(response.key_infos):
+            raise errors.NotFound(reason=_('No archived data.'))
+
+        key_info = response.key_infos[0]
+
+        # retrieve encrypted data from KRA
+        key = kra_client.keys.retrieve_key(
+            key_info.get_key_id(),
+            wrapped_session_key)
+
+        vault['vault_data'] = base64.b64encode(
+            key.encrypted_data).decode('utf-8')
+        vault['nonce'] = base64.b64encode(key.nonce_data).decode('utf-8')
+
+        kra_account.logout()
+
+        response = {}
+        response['result'] = vault
+
+        return response
diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py 
b/ipatests/test_xmlrpc/test_vault_plugin.py
index 
44d397c583928d98ec252899398ae6c3a83c207c..0664addd646806f1b8a5083ef5da16c4dfc015dc
 100644
--- a/ipatests/test_xmlrpc/test_vault_plugin.py
+++ b/ipatests/test_xmlrpc/test_vault_plugin.py
@@ -22,12 +22,15 @@ Test the `ipalib/plugins/vault.py` module.
 """
 
 from ipalib import api, errors
-from xmlrpc_test import Declarative, fuzzy_string
+from xmlrpc_test import Declarative
 
 vault_name = u'test_vault'
 service_name = u'HTTP/server.example.com'
 user_name = u'testuser'
 
+# binary data from \x00 to \xff
+secret = ''.join(map(chr, xrange(0, 256)))
+
 
 class test_vault_plugin(Declarative):
 
@@ -442,4 +445,70 @@ class test_vault_plugin(Declarative):
             },
         },
 
+        {
+            'desc': 'Create vault for archival',
+            'command': (
+                'vault_add',
+                [vault_name],
+                {},
+            ),
+            'expected': {
+                'value': vault_name,
+                'summary': 'Added vault "%s"' % vault_name,
+                'result': {
+                    'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s'
+                          % (vault_name, api.env.basedn),
+                    'objectclass': [u'top', u'ipaVault'],
+                    'cn': [vault_name],
+                },
+            },
+        },
+
+        {
+            'desc': 'Archive secret',
+            'command': (
+                'vault_archive',
+                [vault_name],
+                {
+                    'data': secret,
+                },
+            ),
+            'expected': {
+                'result': {},
+            },
+        },
+
+        {
+            'desc': 'Retrieve secret',
+            'command': (
+                'vault_retrieve',
+                [vault_name],
+                {},
+            ),
+            'expected': {
+                'result': {
+                    'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s'
+                          % (vault_name, api.env.basedn),
+                    'cn': [vault_name],
+                    'data': secret,
+                },
+            },
+        },
+
+        {
+            'desc': 'Delete vault for archival',
+            'command': (
+                'vault_del',
+                [vault_name],
+                {},
+            ),
+            'expected': {
+                'value': [vault_name],
+                'summary': u'Deleted vault "%s"' % vault_name,
+                'result': {
+                    'failed': (),
+                },
+            },
+        },
+
     ]
-- 
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