Please take a look at the new patch.

On 6/2/2015 10:05 AM, Martin Kosek wrote:
4) In the vault-archive forward method, you use "pki" module. However,
this module will be only available on FreeIPA PKI-powered servers and
not on FreeIPA clients - so this will not work unless freeipa-client
gets a dependency on pki-base - which is definitely not something we
want...

In my opinion it should be fine to require pki-base on the client because it
contains just the client library, unless you have other concerns? Any
objections to having pki-nss and pki-cryptography dependencies on the client?

Even if we can change the client code not to depend on "pki" module, since in
this framework the client and server code are written in the same plugin, the
"import pki" still cannot be removed since it's still needed by the server
code, and I don't think conditional import is a good programming practice.

I have major concerns here. Look at the different between installing
"freeipa-client" and "freeipa-client + pki-base" on my F21:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ sudo yum install freeipa-client
...
Install  1 Package (+4 Dependent packages)

Total download size: 2.6 M
Installed size: 14 M
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ sudo yum install freeipa-client pki-base
...
Install  2 Packages (+288 Dependent packages)

Total download size: 160 M
Installed size: 235 M
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is obviously a no-go for client. The conditional import is smaller concern
that big dependency growth on the client. We do them in trust plugin for
example and it works fine (though I agree it is not ideal programming practice).

IMO, we should limit new freeipa-client dependencies only to
python-cryptography (or also python-nss in the worst case, in case
python-cryptography is not enough) - there should be no pki dependencies at
all, these should be only on the server side.

OK. I opened a ticket to split the pki-base into separate Python and Java packages:
https://fedorahosted.org/pki/ticket/1399

For now in this patch I added conditional imports for pki.account and pki.key which are needed to access KRA on the server side. I removed dependency on pki.crypto on the client side and replaced it with direct python-nss code.

On 6/2/2015 1:40 PM, Simo Sorce wrote:
> I have coded in python (jwcrypto)
> support for some key wrapping not yet available in python-cryptography
> and can lend you the code as needed.

Thanks. I'll get back to you when it's time to add support for python-cryptography in KRA:
https://fedorahosted.org/pki/ticket/1400

On 6/2/2015 10:16 AM, Alexander Bokovoy wrote:
Yes, please use conditional import here, it is perfectly valid use case
for the client side.

On 6/2/2015 1:40 PM, Simo Sorce wrote:
> conditional import is just fine

The conditional imports that I've seen usually are used for importing different versions of the same module, which I think is acceptable because the dependency always exists. In the vault case we're selectively importing a module depending on where the code runs. I think that is bad because it adds complexity and it's easy to make mistakes. Any code that depends on that module would have to be (a) guarded:

  if pki_is_loaded:
      ... call pki ...

or (b) used in a method that's only called if the module is loaded:

  def do_something(self): # runs only on server
      ... call pki ...

The (a) is similar to #ifdef's which should be avoidable using OOD, and in (b) we may inadvertently call a wrong method indirectly. I think ideally the client and server code should be in separate files (so they can be deployed separately too), but the framework doesn't seem to allow that.

Regardless, the conditional imports are in.

--
Endi S. Dewata
>From 0e9d3868423c21dc47d125f4b3c23e8261c4655f 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                   | 517 +++++++++++++++++++++++++++++-
 ipatests/test_xmlrpc/test_vault_plugin.py |  71 +++-
 4 files changed, 616 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..ab28ec5cb1179855c2c14e0d72fa25d982924854
 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -17,8 +17,17 @@
 # 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
+
+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 +35,13 @@ 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
+
+if api.env.in_server:
+    import pki.account
+    import pki.key
 
 __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,422 @@ 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
+        current_dbdir = paths.IPA_NSSDB_DIR
+        nss.nss_init(current_dbdir)
+
+        # 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
+        mechanism = nss.CKM_DES3_CBC_PAD
+        slot = nss.get_best_slot(mechanism)
+        key_length = slot.get_best_key_length(mechanism)
+        session_key = slot.key_gen(mechanism, None, key_length)
+
+        # wrap session key with transport certificate
+        public_key = nss_transport_cert.subject_public_key_info.public_key
+        wrapped_session_key = nss.pub_wrap_sym_key(mechanism,
+                                                   public_key,
+                                                   session_key)
+
+        options['session_key'] = base64.b64encode(wrapped_session_key)\
+            .decode('utf-8')
+
+        nonce_length = nss.get_iv_length(mechanism)
+        nonce = nss.generate_random(nonce_length)
+        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
+        iv_si = nss.SecItem(nonce)
+        iv_param = nss.param_from_iv(mechanism, iv_si)
+
+        encoding_ctx = nss.create_context_by_sym_key(mechanism,
+                                                     nss.CKA_ENCRYPT,
+                                                     session_key,
+                                                     iv_param)
+
+        wrapped_vault_data = encoding_ctx.cipher_op(json_vault_data)\
+            + encoding_ctx.digest_final()
+
+        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
+        current_dbdir = paths.IPA_NSSDB_DIR
+        nss.nss_init(current_dbdir)
+
+        # 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
+        mechanism = nss.CKM_DES3_CBC_PAD
+        slot = nss.get_best_slot(mechanism)
+        key_length = slot.get_best_key_length(mechanism)
+        session_key = slot.key_gen(mechanism, None, key_length)
+
+        # wrap session key with transport certificate
+        public_key = nss_transport_cert.subject_public_key_info.public_key
+        wrapped_session_key = nss.pub_wrap_sym_key(mechanism,
+                                                   public_key,
+                                                   session_key)
+
+        # 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'])
+
+        iv_si = nss.SecItem(nonce)
+        iv_param = nss.param_from_iv(mechanism, iv_si)
+
+        decoding_ctx = nss.create_context_by_sym_key(mechanism,
+                                                     nss.CKA_DECRYPT,
+                                                     session_key,
+                                                     iv_param)
+
+        json_vault_data = decoding_ctx.cipher_op(wrapped_vault_data)\
+            + decoding_ctx.digest_final()
+
+        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