summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt52
-rw-r--r--VERSION4
-rw-r--r--freeipa.spec.in2
-rw-r--r--install/share/60basev3.ldif4
-rw-r--r--ipalib/plugins/vault.py583
-rw-r--r--ipatests/test_xmlrpc/test_vault_plugin.py221
6 files changed, 794 insertions, 72 deletions
diff --git a/API.txt b/API.txt
index e226712d3..03a95e646 100644
--- a/API.txt
+++ b/API.txt
@@ -5408,11 +5408,16 @@ output: Output('result', <type 'bool'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
command: vault_add
-args: 1,9,3
-arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True)
+args: 1,14,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
+option: Str('description?', cli_name='desc')
+option: Bytes('ipapublickey?', cli_name='public_key')
+option: Str('ipavaulttype?', cli_name='type')
+option: Str('password?', cli_name='password')
+option: Str('password_file?', cli_name='password_file')
+option: Str('public_key_file?', cli_name='public_key_file')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('service?')
option: Str('setattr*', cli_name='setattr', exclude='webui')
@@ -5422,12 +5427,30 @@ option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: PrimaryKey('value', None, None)
+command: vault_add_internal
+args: 1,10,3
+arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
+option: Bytes('ipapublickey', attribute=True, cli_name='public_key', multivalue=False, required=False)
+option: Bytes('ipavaultsalt', attribute=True, cli_name='salt', multivalue=False, required=False)
+option: Str('ipavaulttype', attribute=True, autofill=True, cli_name='type', default=u'standard', multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('service?')
+option: Flag('shared?', autofill=True, default=False)
+option: Str('user?')
+option: Str('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,8,3
+args: 1,10,3
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Bytes('data?')
option: Str('in?')
+option: Str('password?', cli_name='password')
+option: Str('password_file?', cli_name='password_file')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('service?')
option: Flag('shared?', autofill=True, default=False)
@@ -5436,11 +5459,10 @@ 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_encrypted
-args: 1,10,3
+command: vault_archive_internal
+args: 1,9,3
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
option: Bytes('nonce')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('service?')
@@ -5464,11 +5486,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,11,4
+args: 1,12,4
arg: Str('criteria?', noextrawhitespace=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('cn', attribute=True, autofill=False, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=False)
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
+option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, query=True, required=False)
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('service?')
@@ -5482,12 +5505,15 @@ 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,11,3
+args: 1,14,3
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('delattr*', cli_name='delattr', exclude='webui')
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
+option: Bytes('ipapublickey', attribute=True, autofill=False, cli_name='public_key', multivalue=False, required=False)
+option: Bytes('ipavaultsalt', attribute=True, autofill=False, cli_name='salt', multivalue=False, required=False)
+option: Str('ipavaulttype', attribute=True, autofill=False, cli_name='type', default=u'standard', multivalue=False, required=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Flag('rights', autofill=True, default=False)
option: Str('service?')
@@ -5499,10 +5525,14 @@ 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_retrieve
-args: 1,7,3
+args: 1,11,3
arg: Str('cn', attribute=True, cli_name='name', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.-]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Str('out?')
+option: Str('password?', cli_name='password')
+option: Str('password_file?', cli_name='password_file')
+option: Bytes('private_key?', cli_name='private_key')
+option: Str('private_key_file?', cli_name='private_key_file')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('service?')
option: Flag('shared?', autofill=True, default=False)
@@ -5511,7 +5541,7 @@ 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_encrypted
+command: vault_retrieve_internal
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)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
diff --git a/VERSION b/VERSION
index 266a04af1..3205336c5 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=137
-# Last change: mbabinsk: Commands to manage user/host/service certificates
+IPA_API_VERSION_MINOR=138
+# Last change: edewata - added symmetric and asymmetric vaults
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 52af50dd0..8fee33bd9 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -68,6 +68,7 @@ BuildRequires: python-ldap
BuildRequires: python-setuptools
BuildRequires: python-krbV
BuildRequires: python-nss
+BuildRequires: python-cryptography
BuildRequires: python-netaddr
BuildRequires: python-kerberos >= 1.1-14
BuildRequires: python-rhsm
@@ -293,6 +294,7 @@ Requires: iproute
Requires: keyutils
Requires: pyOpenSSL
Requires: python-nss >= 0.16
+Requires: python-cryptography
Requires: python-lxml
Requires: python-netaddr
Requires: libipa_hbac-python
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 33f4804e3..cb159db05 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -56,6 +56,8 @@ attributeTypes: (2.16.840.1.113730.3.8.11.64 NAME 'ipaSecretKeyRef' DESC 'DN of
attributeTypes: (2.16.840.1.113730.3.8.11.65 NAME 'ipaWrappingMech' DESC 'PKCS#11 wrapping mechanism equivalent to CK_MECHANISM_TYPE' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4.1')
attributeTypes: (2.16.840.1.113730.3.8.11.70 NAME 'ipaPermTargetTo' DESC 'Destination location to move an entry IPA permission ACI' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.0' )
attributeTypes: (2.16.840.1.113730.3.8.11.71 NAME 'ipaPermTargetFrom' DESC 'Source location from where moving an entry IPA permission ACI' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.0' )
+attributeTypes: (2.16.840.1.113730.3.8.18.2.1 NAME 'ipaVaultType' DESC 'IPA vault type' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2')
+attributeTypes: (2.16.840.1.113730.3.8.18.2.2 NAME 'ipaVaultSalt' DESC 'IPA vault salt' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'IPA v4.2' )
objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' )
objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
@@ -79,4 +81,4 @@ objectClasses: (2.16.840.1.113730.3.8.12.24 NAME 'ipaPublicKeyObject' DESC 'Wrap
objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wrapped private keys' SUP top AUXILIARY MUST ( ipaPrivateKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' )
objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' )
objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' )
-objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description ) X-ORIGIN 'IPA v4.2' )
+objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ ipaVaultType $ ipaVaultSalt $ ipaPublicKey ) X-ORIGIN 'IPA v4.2' )
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index f80ecfdfa..193fa5cbb 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -18,11 +18,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
+import getpass
import json
import os
import sys
import tempfile
+from cryptography.fernet import Fernet, InvalidToken
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+from cryptography.hazmat.primitives.asymmetric import padding
+from cryptography.hazmat.primitives.serialization import load_pem_public_key,\
+ load_pem_private_key
+
import nss.nss as nss
import krbV
@@ -50,6 +59,36 @@ Vaults
""") + _("""
Manage vaults.
""") + _("""
+Vault is a secure place to store a secret.
+""") + _("""
+Based on the ownership there are three vault categories:
+* user/private vault
+* service vault
+* shared vault
+""") + _("""
+User vaults are vaults owned used by a particular user. Private
+vaults are vaults owned the current user. Service vaults are
+vaults owned by a service. Shared vaults are owned by the admin
+but they can be used by other users or services.
+""") + _("""
+Based on the security mechanism there are three types of
+vaults:
+* standard vault
+* symmetric vault
+* asymmetric vault
+""") + _("""
+Standard vault uses a secure mechanism to transport and
+store the secret. The secret can only be retrieved by users
+that have access to the vault.
+""") + _("""
+Symmetric vault is similar to the standard vault, but it
+pre-encrypts the secret using a password before transport.
+The secret can only be retrieved using the same password.
+""") + _("""
+Asymmetric vault is similar to the standard vault, but it
+pre-encrypts the secret using a public key before transport.
+The secret can only be retrieved using the private key.
+""") + _("""
EXAMPLES:
""") + _("""
List private vaults:
@@ -76,6 +115,12 @@ EXAMPLES:
Add a user vault:
ipa vault-add <name> --user <username>
""") + _("""
+ Add a symmetric vault:
+ ipa vault-add <name> --type symmetric --password-file password.txt
+""") + _("""
+ Add an asymmetric vault:
+ ipa vault-add <name> --type asymmetric --public-key-file public.pem
+""") + _("""
Show a private vault:
ipa vault-show <name>
""") + _("""
@@ -113,7 +158,7 @@ EXAMPLES:
ipa vault-del <name> --user <username>
""") + _("""
Display vault configuration:
- ipa vault-config
+ ipa vaultconfig-show
""") + _("""
Archive data into private vault:
ipa vault-archive <name> --in <input file>
@@ -127,6 +172,12 @@ EXAMPLES:
Archive data into user vault:
ipa vault-archive <name> --user <username> --in <input file>
""") + _("""
+ Archive data into symmetric vault:
+ ipa vault-archive <name> --in <input file>
+""") + _("""
+ Archive data into asymmetric vault:
+ ipa vault-archive <name> --in <input file>
+""") + _("""
Retrieve data from private vault:
ipa vault-retrieve <name> --out <output file>
""") + _("""
@@ -137,7 +188,13 @@ EXAMPLES:
ipa vault-retrieve <name> --shared --out <output file>
""") + _("""
Retrieve data from user vault:
- ipa vault-retrieve <name> --user <user name> --out <output file>
+ ipa vault-retrieve <name> --user <username> --out <output file>
+""") + _("""
+ Retrieve data from symmetric vault:
+ ipa vault-retrieve <name> --out data.bin
+""") + _("""
+ Retrieve data from asymmetric vault:
+ ipa vault-retrieve <name> --out data.bin --private-key-file private.pem
""")
register = Registry()
@@ -146,7 +203,7 @@ register = Registry()
vault_options = (
Str(
'service?',
- doc=_('Service name'),
+ doc=_('Service name of the service vault'),
),
Flag(
'shared?',
@@ -154,7 +211,7 @@ vault_options = (
),
Str(
'user?',
- doc=_('Username'),
+ doc=_('Username of the user vault'),
),
)
@@ -174,6 +231,14 @@ class vault(LDAPObject):
default_attributes = [
'cn',
'description',
+ 'ipavaulttype',
+ 'ipavaultsalt',
+ 'ipapublickey',
+ ]
+ search_display_attributes = [
+ 'cn',
+ 'description',
+ 'ipavaulttype',
]
label = _('Vaults')
@@ -195,6 +260,28 @@ class vault(LDAPObject):
label=_('Description'),
doc=_('Vault description'),
),
+ Str(
+ 'ipavaulttype?',
+ cli_name='type',
+ label=_('Type'),
+ doc=_('Vault type'),
+ default=u'standard',
+ autofill=True,
+ ),
+ Bytes(
+ 'ipavaultsalt?',
+ cli_name='salt',
+ label=_('Salt'),
+ doc=_('Vault salt'),
+ flags=['no_search'],
+ ),
+ Bytes(
+ 'ipapublickey?',
+ cli_name='public_key',
+ label=_('Public key'),
+ doc=_('Vault public key'),
+ flags=['no_search'],
+ ),
)
def get_dn(self, *keys, **options):
@@ -307,12 +394,232 @@ class vault(LDAPObject):
return 'ipa:' + id
+ def get_new_password(self):
+ """
+ Gets new password from user and verify it.
+ """
+ while True:
+ password = getpass.getpass('New password: ').decode(
+ sys.stdin.encoding)
+ password2 = getpass.getpass('Verify password: ').decode(
+ sys.stdin.encoding)
+
+ if password == password2:
+ return password
+
+ print ' ** Passwords do not match! **'
+
+ def get_existing_password(self, new=False):
+ """
+ Gets existing password from user.
+ """
+ return getpass.getpass('Password: ').decode(sys.stdin.encoding)
+
+ def generate_symmetric_key(self, password, salt):
+ """
+ Generates symmetric key from password and salt.
+ """
+ kdf = PBKDF2HMAC(
+ algorithm=hashes.SHA256(),
+ length=32,
+ salt=salt,
+ iterations=100000,
+ backend=default_backend()
+ )
+
+ return base64.b64encode(kdf.derive(password.encode('utf-8')))
+
+ def encrypt(self, data, symmetric_key=None, public_key=None):
+ """
+ Encrypts data with symmetric key or public key.
+ """
+ if symmetric_key:
+ fernet = Fernet(symmetric_key)
+ return fernet.encrypt(data)
+
+ elif public_key:
+ rsa_public_key = load_pem_public_key(
+ data=public_key,
+ backend=default_backend()
+ )
+ return rsa_public_key.encrypt(
+ data,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ )
+
+ def decrypt(self, data, symmetric_key=None, private_key=None):
+ """
+ Decrypts data with symmetric key or public key.
+ """
+ if symmetric_key:
+ try:
+ fernet = Fernet(symmetric_key)
+ return fernet.decrypt(data)
+ except InvalidToken:
+ raise errors.AuthenticationError(
+ message=_('Invalid credentials'))
+
+ elif private_key:
+ try:
+ rsa_private_key = load_pem_private_key(
+ data=private_key,
+ password=None,
+ backend=default_backend()
+ )
+ return rsa_private_key.decrypt(
+ data,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA1()),
+ algorithm=hashes.SHA1(),
+ label=None
+ )
+ )
+ except AssertionError:
+ raise errors.AuthenticationError(
+ message=_('Invalid credentials'))
+
@register()
-class vault_add(LDAPCreate):
+class vault_add(PKQuery, Local):
__doc__ = _('Create a new vault.')
- takes_options = LDAPCreate.takes_options + vault_options
+ takes_options = LDAPCreate.takes_options + vault_options + (
+ Str(
+ 'description?',
+ cli_name='desc',
+ doc=_('Vault description'),
+ ),
+ Str(
+ 'ipavaulttype?',
+ cli_name='type',
+ doc=_('Vault type'),
+ ),
+ Str(
+ 'password?',
+ cli_name='password',
+ doc=_('Vault password'),
+ ),
+ Str( # TODO: use File parameter
+ 'password_file?',
+ cli_name='password_file',
+ doc=_('File containing the vault password'),
+ ),
+ Bytes(
+ 'ipapublickey?',
+ cli_name='public_key',
+ doc=_('Vault public key'),
+ ),
+ Str( # TODO: use File parameter
+ 'public_key_file?',
+ cli_name='public_key_file',
+ doc=_('File containing the vault public key'),
+ ),
+ )
+
+ has_output = output.standard_entry
+
+ def forward(self, *args, **options):
+
+ vault_type = options.get('ipavaulttype', u'standard')
+ password = options.get('password')
+ password_file = options.get('password_file')
+ public_key = options.get('ipapublickey')
+ public_key_file = options.get('public_key_file')
+
+ # don't send these parameters to server
+ if 'password' in options:
+ del options['password']
+ if 'password_file' in options:
+ del options['password_file']
+ if 'public_key_file' in options:
+ del options['public_key_file']
+
+ if self.api.env.in_server:
+ backend = self.api.Backend.ldap2
+ else:
+ backend = self.api.Backend.rpcclient
+ if not backend.isconnected():
+ backend.connect(ccache=krbV.default_context().default_ccache())
+
+ if vault_type == u'standard':
+
+ pass
+
+ elif vault_type == u'symmetric':
+
+ # get password
+ if password and password_file:
+ raise errors.MutuallyExclusiveError(
+ reason=_('Password specified multiple times'))
+
+ elif password:
+ pass
+
+ elif password_file:
+ with open(password_file, 'rb') as f:
+ password = f.read().rstrip('\n').decode('utf-8')
+
+ else:
+ password = self.obj.get_new_password()
+
+ # generate vault salt
+ options['ipavaultsalt'] = os.urandom(16)
+
+ elif vault_type == u'asymmetric':
+
+ # get new vault public key
+ if public_key and public_key_file:
+ raise errors.MutuallyExclusiveError(
+ reason=_('Public key specified multiple times'))
+
+ elif public_key:
+ pass
+
+ elif public_key_file:
+ with open(public_key_file, 'rb') as f:
+ public_key = f.read()
+
+ # store vault public key
+ options['ipapublickey'] = public_key
+
+ else:
+ raise errors.ValidationError(
+ name='ipapublickey',
+ error=_('Missing vault public key'))
+
+ # create vault
+ response = self.api.Command.vault_add_internal(*args, **options)
+
+ # prepare parameters for archival
+ opts = options.copy()
+ if 'description' in opts:
+ del opts['description']
+ if 'ipavaulttype' in opts:
+ del opts['ipavaulttype']
+
+ if vault_type == u'symmetric':
+ opts['password'] = password
+ del opts['ipavaultsalt']
+
+ elif vault_type == u'asymmetric':
+ del opts['ipapublickey']
+
+ # archive blank data
+ self.api.Command.vault_archive(*args, **opts)
+
+ return response
+
+
+@register()
+class vault_add_internal(LDAPCreate):
+
+ NO_CLI = True
+
+ takes_options = vault_options
msg_summary = _('Added vault "%(value)s"')
@@ -513,29 +820,46 @@ class vault_archive(PKQuery, Local):
'in?',
doc=_('File containing data to archive'),
),
+ Str(
+ 'password?',
+ cli_name='password',
+ doc=_('Vault password'),
+ ),
+ Str( # TODO: use File parameter
+ 'password_file?',
+ cli_name='password_file',
+ doc=_('File containing the vault password'),
+ ),
)
has_output = output.standard_entry
- msg_summary = _('Archived data into vault "%(value)s"')
-
def forward(self, *args, **options):
+ name = args[-1]
+
data = options.get('data')
input_file = options.get('in')
+ password = options.get('password')
+ password_file = options.get('password_file')
+
# don't send these parameters to server
if 'data' in options:
del options['data']
if 'in' in options:
del options['in']
+ if 'password' in options:
+ del options['password']
+ if 'password_file' in options:
+ del options['password_file']
# get data
if data and input_file:
raise errors.MutuallyExclusiveError(
reason=_('Input data specified multiple times'))
- if input_file:
+ elif input_file:
with open(input_file, 'rb') as f:
data = f.read()
@@ -549,13 +873,77 @@ class vault_archive(PKQuery, Local):
if not backend.isconnected():
backend.connect(ccache=krbV.default_context().default_ccache())
+ # retrieve vault info
+ vault = self.api.Command.vault_show(*args, **options)['result']
+
+ vault_type = vault['ipavaulttype'][0]
+
+ if vault_type == u'standard':
+
+ encrypted_key = None
+
+ elif vault_type == u'symmetric':
+
+ # get password
+ if password and password_file:
+ raise errors.MutuallyExclusiveError(
+ reason=_('Password specified multiple times'))
+
+ elif password:
+ pass
+
+ elif password_file:
+ with open(password_file) as f:
+ password = f.read().rstrip('\n').decode('utf-8')
+
+ else:
+ password = self.obj.get_existing_password()
+
+ # verify password by retrieving existing data
+ opts = options.copy()
+ opts['password'] = password
+ try:
+ self.api.Command.vault_retrieve(*args, **opts)
+ except errors.NotFound:
+ pass
+
+ salt = vault['ipavaultsalt'][0]
+
+ # generate encryption key from vault password
+ encryption_key = self.obj.generate_symmetric_key(
+ password, salt)
+
+ # encrypt data with encryption key
+ data = self.obj.encrypt(data, symmetric_key=encryption_key)
+
+ encrypted_key = None
+
+ elif vault_type == u'asymmetric':
+
+ public_key = vault['ipapublickey'][0].encode('utf-8')
+
+ # generate encryption key
+ encryption_key = base64.b64encode(os.urandom(32))
+
+ # encrypt data with encryption key
+ data = self.obj.encrypt(data, symmetric_key=encryption_key)
+
+ # encrypt encryption key with public key
+ encrypted_key = self.obj.encrypt(
+ encryption_key, public_key=public_key)
+
+ else:
+ raise errors.ValidationError(
+ name='vault_type',
+ error=_('Invalid vault type'))
+
# initialize NSS database
current_dbdir = paths.IPA_NSSDB_DIR
nss.nss_init(current_dbdir)
# retrieve transport certificate
- config = self.api.Command.vaultconfig_show()
- transport_cert_der = config['result']['transport_cert']
+ config = self.api.Command.vaultconfig_show()['result']
+ transport_cert_der = config['transport_cert']
nss_transport_cert = nss.Certificate(transport_cert_der)
# generate session key
@@ -579,6 +967,10 @@ class vault_archive(PKQuery, Local):
vault_data = {}
vault_data[u'data'] = base64.b64encode(data).decode('utf-8')
+ if encrypted_key:
+ vault_data[u'encrypted_key'] = base64.b64encode(encrypted_key)\
+ .decode('utf-8')
+
json_vault_data = json.dumps(vault_data)
# wrap vault_data with session key
@@ -595,16 +987,12 @@ class vault_archive(PKQuery, Local):
options['vault_data'] = wrapped_vault_data
- response = self.api.Command.vault_archive_encrypted(*args, **options)
-
- response['result'] = {}
- del response['summary']
-
- return response
+ return self.api.Command.vault_archive_internal(*args, **options)
@register()
-class vault_archive_encrypted(Update):
+class vault_archive_internal(PKQuery):
+
NO_CLI = True
takes_options = vault_options + (
@@ -622,6 +1010,10 @@ class vault_archive_encrypted(Update):
),
)
+ has_output = output.standard_entry
+
+ msg_summary = _('Archived data into vault "%(value)s"')
+
def execute(self, *args, **options):
if not self.api.Command.kra_is_enabled()['result']:
@@ -633,8 +1025,7 @@ class vault_archive_encrypted(Update):
wrapped_session_key = options.pop('session_key')
# retrieve vault info
- result = self.api.Command.vault_show(*args, **options)
- vault = result['result']
+ vault = self.api.Command.vault_show(*args, **options)['result']
# connect to KRA
kra_client = self.api.Backend.kra.get_client()
@@ -666,7 +1057,14 @@ class vault_archive_encrypted(Update):
kra_account.logout()
- return result
+ response = {
+ 'value': args[-1],
+ 'result': {},
+ }
+
+ response['summary'] = self.msg_summary % response
+
+ return response
@register()
@@ -678,6 +1076,26 @@ class vault_retrieve(PKQuery, Local):
'out?',
doc=_('File to store retrieved data'),
),
+ Str(
+ 'password?',
+ cli_name='password',
+ doc=_('Vault password'),
+ ),
+ Str( # TODO: use File parameter
+ 'password_file?',
+ cli_name='password_file',
+ doc=_('File containing the vault password'),
+ ),
+ Bytes(
+ 'private_key?',
+ cli_name='private_key',
+ doc=_('Vault private key'),
+ ),
+ Str( # TODO: use File parameter
+ 'private_key_file?',
+ cli_name='private_key_file',
+ doc=_('File containing the vault private key'),
+ ),
)
has_output = output.standard_entry
@@ -688,15 +1106,28 @@ class vault_retrieve(PKQuery, Local):
),
)
- msg_summary = _('Retrieved data from vault "%(value)s"')
-
def forward(self, *args, **options):
+ name = args[-1]
+
output_file = options.get('out')
+ password = options.get('password')
+ password_file = options.get('password_file')
+ private_key = options.get('private_key')
+ private_key_file = options.get('private_key_file')
+
# don't send these parameters to server
if 'out' in options:
del options['out']
+ if 'password' in options:
+ del options['password']
+ if 'password_file' in options:
+ del options['password_file']
+ if 'private_key' in options:
+ del options['private_key']
+ if 'private_key_file' in options:
+ del options['private_key_file']
if self.api.env.in_server:
backend = self.api.Backend.ldap2
@@ -705,13 +1136,18 @@ class vault_retrieve(PKQuery, Local):
if not backend.isconnected():
backend.connect(ccache=krbV.default_context().default_ccache())
+ # retrieve vault info
+ vault = self.api.Command.vault_show(*args, **options)['result']
+
+ vault_type = vault['ipavaulttype'][0]
+
# initialize NSS database
current_dbdir = paths.IPA_NSSDB_DIR
nss.nss_init(current_dbdir)
# retrieve transport certificate
- config = self.api.Command.vaultconfig_show()
- transport_cert_der = config['result']['transport_cert']
+ config = self.api.Command.vaultconfig_show()['result']
+ transport_cert_der = config['transport_cert']
nss_transport_cert = nss.Certificate(transport_cert_der)
# generate session key
@@ -729,7 +1165,7 @@ class vault_retrieve(PKQuery, Local):
# send retrieval request to server
options['session_key'] = wrapped_session_key.data
- response = self.api.Command.vault_retrieve_encrypted(*args, **options)
+ response = self.api.Command.vault_retrieve_internal(*args, **options)
result = response['result']
nonce = result['nonce']
@@ -751,18 +1187,85 @@ class vault_retrieve(PKQuery, Local):
vault_data = json.loads(json_vault_data)
data = base64.b64decode(vault_data[u'data'].encode('utf-8'))
+ encrypted_key = None
+
+ if 'encrypted_key' in vault_data:
+ encrypted_key = base64.b64decode(vault_data[u'encrypted_key']
+ .encode('utf-8'))
+
+ if vault_type == u'standard':
+
+ pass
+
+ elif vault_type == u'symmetric':
+
+ salt = vault['ipavaultsalt'][0]
+
+ # get encryption key from vault password
+ if password and password_file:
+ raise errors.MutuallyExclusiveError(
+ reason=_('Password specified multiple times'))
+
+ elif password:
+ pass
+
+ elif password_file:
+ with open(password_file) as f:
+ password = f.read().rstrip('\n').decode('utf-8')
+
+ else:
+ password = self.obj.get_existing_password()
+
+ # generate encryption key from password
+ encryption_key = self.obj.generate_symmetric_key(password, salt)
+
+ # decrypt data with encryption key
+ data = self.obj.decrypt(data, symmetric_key=encryption_key)
+
+ elif vault_type == u'asymmetric':
+
+ # get encryption key with vault private key
+ if private_key and private_key_file:
+ raise errors.MutuallyExclusiveError(
+ reason=_('Private key specified multiple times'))
+
+ elif private_key:
+ pass
+
+ elif private_key_file:
+ with open(private_key_file, 'rb') as f:
+ private_key = f.read()
+
+ else:
+ raise errors.ValidationError(
+ name='private_key',
+ error=_('Missing vault private key'))
+
+ # decrypt encryption key with private key
+ encryption_key = self.obj.decrypt(
+ encrypted_key, private_key=private_key)
+
+ # decrypt data with encryption key
+ data = self.obj.decrypt(data, symmetric_key=encryption_key)
+
+ else:
+ raise errors.ValidationError(
+ name='vault_type',
+ error=_('Invalid vault type'))
+
if output_file:
with open(output_file, 'w') as f:
f.write(data)
- response['result'] = {'data': data}
- del response['summary']
+ else:
+ response['result'] = {'data': data}
return response
@register()
-class vault_retrieve_encrypted(Retrieve):
+class vault_retrieve_internal(PKQuery):
+
NO_CLI = True
takes_options = vault_options + (
@@ -772,6 +1275,10 @@ class vault_retrieve_encrypted(Retrieve):
),
)
+ has_output = output.standard_entry
+
+ msg_summary = _('Retrieved data from vault "%(value)s"')
+
def execute(self, *args, **options):
if not self.api.Command.kra_is_enabled()['result']:
@@ -781,8 +1288,7 @@ class vault_retrieve_encrypted(Retrieve):
wrapped_session_key = options.pop('session_key')
# retrieve vault info
- result = self.api.Command.vault_show(*args, **options)
- vault = result['result']
+ vault = self.api.Command.vault_show(*args, **options)['result']
# connect to KRA
kra_client = self.api.Backend.kra.get_client()
@@ -807,12 +1313,19 @@ class vault_retrieve_encrypted(Retrieve):
key_info.get_key_id(),
wrapped_session_key)
- vault['vault_data'] = key.encrypted_data
- vault['nonce'] = key.nonce_data
-
kra_account.logout()
- return result
+ response = {
+ 'value': args[-1],
+ 'result': {
+ 'vault_data': key.encrypted_data,
+ 'nonce': key.nonce_data,
+ },
+ }
+
+ response['summary'] = self.msg_summary % response
+
+ return response
@register()
diff --git a/ipatests/test_xmlrpc/test_vault_plugin.py b/ipatests/test_xmlrpc/test_vault_plugin.py
index 9a40547b1..f8b57855a 100644
--- a/ipatests/test_xmlrpc/test_vault_plugin.py
+++ b/ipatests/test_xmlrpc/test_vault_plugin.py
@@ -22,15 +22,63 @@ Test the `ipalib/plugins/vault.py` module.
"""
from ipalib import api, errors
-from xmlrpc_test import Declarative
+from xmlrpc_test import Declarative, fuzzy_string
vault_name = u'test_vault'
service_name = u'HTTP/server.example.com'
user_name = u'testuser'
+standard_vault_name = u'standard_test_vault'
+symmetric_vault_name = u'symmetric_test_vault'
+asymmetric_vault_name = u'asymmetric_test_vault'
+
# binary data from \x00 to \xff
secret = ''.join(map(chr, xrange(0, 256)))
+password = u'password'
+
+public_key = """
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnT61EFxUOQgCJdM0tmw/
+pRRPDPGchTClnU1eBtiQD3ItKYf1+weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDe
+k/zeB6nSVdk47OdaW1AHrJL+44r238Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqB
+tsxXaaAgjMp0AGq2U/aO/akeEYWQOYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7N
+otG4eR6c2o9Fyjd+M4Gai5Ce0fSrigRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLO
+pVThop+Xivcre3SpI0kt6oZPhBw9i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG6
+3wIDAQAB
+-----END PUBLIC KEY-----
+"""
+
+private_key = """
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAnT61EFxUOQgCJdM0tmw/pRRPDPGchTClnU1eBtiQD3ItKYf1
++weMGwGOSJXPtkto7NlE7Qs8WHAr0UjyeBDek/zeB6nSVdk47OdaW1AHrJL+44r2
+38Jbm/+7VO5lTu6Z4N5p0VqoWNLi0Uh/CkqBtsxXaaAgjMp0AGq2U/aO/akeEYWQ
+OYIdqUKVgAEKX5MmIA8tmbmoYIQ+B4Q3vX7NotG4eR6c2o9Fyjd+M4Gai5Ce0fSr
+igRvxAYi8xpRkQ5yQn5gf4WVrn+UKTfOIjLOpVThop+Xivcre3SpI0kt6oZPhBw9
+i8gbMnqifVmGFpVdhq+QVBqp+MVJvTbhRPG63wIDAQABAoIBAQCD2bXnfxPcMnvi
+jaPwpvoDCPF0EBBHmk/0g5ApO2Qon3uBDJFUqbJwXrCY6o2d9MOJfnGONlKmcYA8
+X+d4h+SqwGjIkjxdYeSauS+Jy6Rzr1ptH/P8EjPQrfG9uJxYQDflV3nxYwwwVrx7
+8kccMPdteRB+8Bb7FzOHufMimmayCNFETnVT5CKH2PrYoPB+fr0itCipWOenDp33
+e73OV+K9U3rclmtHaoRxGohqByKfQRUkipjw4m+T3qfZZc5eN77RGW8J+oL1GVom
+fwtiH7N1HVte0Dmd13nhiASg355kjqRPcIMPsRHvXkOpgg5HRUTKG5elqAyvvm27
+Fzj1YdeRAoGBAMnE61+FYh8qCyEGe8r6RGjO8iuoyk1t+0gBWbmILLBiRnj4K8Tc
+k7HBG/pg3XCNbCuRwiLg8tk3VAAXzn6o+IJr3QnKbNCGa1lKfYU4mt11sBEyuL5V
+NpZcZ8IiPhMlGyDA9cFbTMKOE08RqbOIdxOmTizFt0R5sYZAwOjEvBIZAoGBAMeC
+N/P0bdrScFZGeS51wEdiWme/CO0IyGoqU6saI8L0dbmMJquiaAeIEjIKLqxH1RON
+axhsyk97e0PCcc5QK62Utf50UUAbL/v7CpIG+qdSRYDO4bVHSCkwF32N3pYh/iVU
+EsEBEkZiJi0dWa/0asDbsACutxcHda3RI5pi7oO3AoGAcbGNs/CUHt1xEfX2UaT+
+YVSjb2iYPlNH8gYYygvqqqVl8opdF3v3mYUoP8jPXrnCBzcF/uNk1HNx2O+RQxvx
+lIQ1NGwlLsdfvBvWaPhBg6LqSHadVVrs/IMrUGA9PEp/Y9B3arIIqeSnCrn4Nxsh
+higDCwWKRIKSPwVD7qXVGBkCgYEAu5/CASIRIeYgEXMLSd8hKcDcJo8o1MoauIT/
+1Hyrvw9pm0qrn2QHk3WrLvYWeJzBTTcEzZ6aEG+fN9UodA8/VGnzUc6QDsrCsKWh
+hj0cArlDdeSZrYLQ4TNCFCiUePqU6QQM8weP6TMqlejxTKF+t8qi1bF5rCWuzP1P
+D0UU7DcCgYAUvmEGckugS+FTatop8S/rmkcQ4Bf5M/YCZfsySavucDiHcBt0QtXt
+Swh0XdDsYS3W1yj2XqqsQ7R58KNaffCHjjulWFzb5IiuSvvdxzWtiXHisOpO36MJ
+kUlCMj24a8XsShzYTWBIyW2ngvGe3pQ9PfjkUdm0LGZjYITCBvgOKw==
+-----END RSA PRIVATE KEY-----
+"""
+
class test_vault_plugin(Declarative):
@@ -42,6 +90,9 @@ class test_vault_plugin(Declarative):
}),
('vault_del', [vault_name], {'shared': True, 'continue': True}),
('vault_del', [vault_name], {'user': user_name, 'continue': True}),
+ ('vault_del', [standard_vault_name], {'continue': True}),
+ ('vault_del', [symmetric_vault_name], {'continue': True}),
+ ('vault_del', [asymmetric_vault_name], {'continue': True}),
]
tests = [
@@ -61,6 +112,7 @@ class test_vault_plugin(Declarative):
% (vault_name, api.env.basedn),
'objectclass': [u'top', u'ipaVault'],
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -81,6 +133,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,cn=kra,%s'
% (vault_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
],
},
@@ -100,6 +153,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,cn=kra,%s'
% (vault_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -119,6 +173,7 @@ class test_vault_plugin(Declarative):
'result': {
'cn': [vault_name],
'description': [u'Test vault'],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -156,6 +211,7 @@ class test_vault_plugin(Declarative):
% (vault_name, service_name, api.env.basedn),
'objectclass': [u'top', u'ipaVault'],
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -178,6 +234,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=%s,cn=services,cn=vaults,cn=kra,%s'
% (vault_name, service_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
],
},
@@ -199,6 +256,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=%s,cn=services,cn=vaults,cn=kra,%s'
% (vault_name, service_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -219,6 +277,7 @@ class test_vault_plugin(Declarative):
'result': {
'cn': [vault_name],
'description': [u'Test vault'],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -258,6 +317,7 @@ class test_vault_plugin(Declarative):
% (vault_name, api.env.basedn),
'objectclass': [u'top', u'ipaVault'],
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -280,6 +340,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=shared,cn=vaults,cn=kra,%s'
% (vault_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
],
},
@@ -301,6 +362,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=shared,cn=vaults,cn=kra,%s'
% (vault_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -321,6 +383,7 @@ class test_vault_plugin(Declarative):
'result': {
'cn': [vault_name],
'description': [u'Test vault'],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -360,6 +423,7 @@ class test_vault_plugin(Declarative):
% (vault_name, user_name, api.env.basedn),
'objectclass': [u'top', u'ipaVault'],
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -382,6 +446,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=%s,cn=users,cn=vaults,cn=kra,%s'
% (vault_name, user_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
],
},
@@ -403,6 +468,7 @@ class test_vault_plugin(Declarative):
'dn': u'cn=%s,cn=%s,cn=users,cn=vaults,cn=kra,%s'
% (vault_name, user_name, api.env.basedn),
'cn': [vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -423,6 +489,7 @@ class test_vault_plugin(Declarative):
'result': {
'cn': [vault_name],
'description': [u'Test vault'],
+ 'ipavaulttype': [u'standard'],
},
},
},
@@ -446,50 +513,53 @@ class test_vault_plugin(Declarative):
},
{
- 'desc': 'Create vault for archival',
+ 'desc': 'Create standard vault',
'command': (
'vault_add',
- [vault_name],
+ [standard_vault_name],
{},
),
'expected': {
- 'value': vault_name,
- 'summary': 'Added vault "%s"' % vault_name,
+ 'value': standard_vault_name,
+ 'summary': 'Added vault "%s"' % standard_vault_name,
'result': {
- 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,%s'
- % (vault_name, api.env.basedn),
+ 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,cn=kra,%s'
+ % (standard_vault_name, api.env.basedn),
'objectclass': [u'top', u'ipaVault'],
- 'cn': [vault_name],
+ 'cn': [standard_vault_name],
+ 'ipavaulttype': [u'standard'],
},
},
},
{
- 'desc': 'Archive secret',
+ 'desc': 'Archive secret into standard vault',
'command': (
'vault_archive',
- [vault_name],
+ [standard_vault_name],
{
'data': secret,
},
),
'expected': {
- 'value': vault_name,
- 'summary': 'Archived data into vault "%s"' % vault_name,
+ 'value': standard_vault_name,
+ 'summary': 'Archived data into vault "%s"'
+ % standard_vault_name,
'result': {},
},
},
{
- 'desc': 'Retrieve secret',
+ 'desc': 'Retrieve secret from standard vault',
'command': (
'vault_retrieve',
- [vault_name],
+ [standard_vault_name],
{},
),
'expected': {
- 'value': vault_name,
- 'summary': 'Retrieved data from vault "%s"' % vault_name,
+ 'value': standard_vault_name,
+ 'summary': 'Retrieved data from vault "%s"'
+ % standard_vault_name,
'result': {
'data': secret,
},
@@ -497,17 +567,122 @@ class test_vault_plugin(Declarative):
},
{
- 'desc': 'Delete vault for archival',
+ 'desc': 'Create symmetric vault',
'command': (
- 'vault_del',
- [vault_name],
- {},
+ 'vault_add',
+ [symmetric_vault_name],
+ {
+ 'ipavaulttype': u'symmetric',
+ 'password': password,
+ },
),
'expected': {
- 'value': [vault_name],
- 'summary': u'Deleted vault "%s"' % vault_name,
+ 'value': symmetric_vault_name,
+ 'summary': 'Added vault "%s"' % symmetric_vault_name,
'result': {
- 'failed': (),
+ 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,cn=kra,%s'
+ % (symmetric_vault_name, api.env.basedn),
+ 'objectclass': [u'top', u'ipaVault'],
+ 'cn': [symmetric_vault_name],
+ 'ipavaulttype': [u'symmetric'],
+ 'ipavaultsalt': [fuzzy_string],
+ },
+ },
+ },
+
+ {
+ 'desc': 'Archive secret into symmetric vault',
+ 'command': (
+ 'vault_archive',
+ [symmetric_vault_name],
+ {
+ 'password': password,
+ 'data': secret,
+ },
+ ),
+ 'expected': {
+ 'value': symmetric_vault_name,
+ 'summary': 'Archived data into vault "%s"'
+ % symmetric_vault_name,
+ 'result': {},
+ },
+ },
+
+ {
+ 'desc': 'Retrieve secret from symmetric vault',
+ 'command': (
+ 'vault_retrieve',
+ [symmetric_vault_name],
+ {
+ 'password': password,
+ },
+ ),
+ 'expected': {
+ 'value': symmetric_vault_name,
+ 'summary': 'Retrieved data from vault "%s"'
+ % symmetric_vault_name,
+ 'result': {
+ 'data': secret,
+ },
+ },
+ },
+
+ {
+ 'desc': 'Create asymmetric vault',
+ 'command': (
+ 'vault_add',
+ [asymmetric_vault_name],
+ {
+ 'ipavaulttype': u'asymmetric',
+ 'ipapublickey': public_key,
+ },
+ ),
+ 'expected': {
+ 'value': asymmetric_vault_name,
+ 'summary': 'Added vault "%s"' % asymmetric_vault_name,
+ 'result': {
+ 'dn': u'cn=%s,cn=admin,cn=users,cn=vaults,cn=kra,%s'
+ % (asymmetric_vault_name, api.env.basedn),
+ 'objectclass': [u'top', u'ipaVault'],
+ 'cn': [asymmetric_vault_name],
+ 'ipavaulttype': [u'asymmetric'],
+ 'ipapublickey': [public_key],
+ },
+ },
+ },
+
+ {
+ 'desc': 'Archive secret into asymmetric vault',
+ 'command': (
+ 'vault_archive',
+ [asymmetric_vault_name],
+ {
+ 'data': secret,
+ },
+ ),
+ 'expected': {
+ 'value': asymmetric_vault_name,
+ 'summary': 'Archived data into vault "%s"'
+ % asymmetric_vault_name,
+ 'result': {},
+ },
+ },
+
+ {
+ 'desc': 'Retrieve secret from asymmetric vault',
+ 'command': (
+ 'vault_retrieve',
+ [asymmetric_vault_name],
+ {
+ 'private_key': private_key,
+ },
+ ),
+ 'expected': {
+ 'value': asymmetric_vault_name,
+ 'summary': 'Retrieved data from vault "%s"'
+ % asymmetric_vault_name,
+ 'result': {
+ 'data': secret,
},
},
},