acme_account: add support for External Account Binding (#100)
* acme_account: add support for External Account Binding. * Add changelog fragment. * Error if externalAccountRequired is set in ACME directory meta, but external account data is not provided. * Validate that EAB key is Base64URL encoded. * Improve documentation. * Add padding to Base64 encoded key if necessary. * Make account creation idempotent with ZeroSSL.pull/63/head
parent
2f59d44f9e
commit
d03e723fe0
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- "acme_account - add ``external_account_binding`` option to allow creation of ACME accounts with External Account Binding (https://github.com/ansible-collections/community.crypto/issues/89)."
|
|
@ -38,12 +38,13 @@ from ansible_collections.community.crypto.plugins.module_utils.compat import ipa
|
||||||
try:
|
try:
|
||||||
import cryptography
|
import cryptography
|
||||||
import cryptography.hazmat.backends
|
import cryptography.hazmat.backends
|
||||||
import cryptography.hazmat.primitives.serialization
|
import cryptography.hazmat.primitives.hashes
|
||||||
import cryptography.hazmat.primitives.asymmetric.rsa
|
import cryptography.hazmat.primitives.hmac
|
||||||
import cryptography.hazmat.primitives.asymmetric.ec
|
import cryptography.hazmat.primitives.asymmetric.ec
|
||||||
import cryptography.hazmat.primitives.asymmetric.padding
|
import cryptography.hazmat.primitives.asymmetric.padding
|
||||||
import cryptography.hazmat.primitives.hashes
|
import cryptography.hazmat.primitives.asymmetric.rsa
|
||||||
import cryptography.hazmat.primitives.asymmetric.utils
|
import cryptography.hazmat.primitives.asymmetric.utils
|
||||||
|
import cryptography.hazmat.primitives.serialization
|
||||||
import cryptography.x509
|
import cryptography.x509
|
||||||
import cryptography.x509.oid
|
import cryptography.x509.oid
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
@ -271,9 +272,42 @@ def _parse_key_openssl(openssl_binary, module, key_file=None, key_content=None):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _create_mac_key_openssl(openssl_bin, module, alg, key):
|
||||||
|
if alg == 'HS256':
|
||||||
|
hashalg = 'sha256'
|
||||||
|
hashbytes = 32
|
||||||
|
elif alg == 'HS384':
|
||||||
|
hashalg = 'sha384'
|
||||||
|
hashbytes = 48
|
||||||
|
elif alg == 'HS512':
|
||||||
|
hashalg = 'sha512'
|
||||||
|
hashbytes = 64
|
||||||
|
else:
|
||||||
|
raise ModuleFailException('Unsupported MAC key algorithm for OpenSSL backend: {0}'.format(alg))
|
||||||
|
key_bytes = base64.urlsafe_b64decode(key)
|
||||||
|
if len(key_bytes) < hashbytes:
|
||||||
|
raise ModuleFailException(
|
||||||
|
'{0} key must be at least {1} bytes long (after Base64 decoding)'.format(alg, hashbytes))
|
||||||
|
return {
|
||||||
|
'type': 'hmac',
|
||||||
|
'alg': alg,
|
||||||
|
'jwk': {
|
||||||
|
'kty': 'oct',
|
||||||
|
'k': key,
|
||||||
|
},
|
||||||
|
'hash': hashalg,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _sign_request_openssl(openssl_binary, module, payload64, protected64, key_data):
|
def _sign_request_openssl(openssl_binary, module, payload64, protected64, key_data):
|
||||||
openssl_sign_cmd = [openssl_binary, "dgst", "-{0}".format(key_data['hash']), "-sign", key_data['key_file']]
|
|
||||||
sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
|
sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
|
||||||
|
if key_data['type'] == 'hmac':
|
||||||
|
hex_key = to_native(binascii.hexlify(base64.urlsafe_b64decode(key_data['jwk']['k'])))
|
||||||
|
cmd_postfix = ["-mac", "hmac", "-macopt", "hexkey:{0}".format(hex_key), "-binary"]
|
||||||
|
else:
|
||||||
|
cmd_postfix = ["-sign", key_data['key_file']]
|
||||||
|
openssl_sign_cmd = [openssl_binary, "dgst", "-{0}".format(key_data['hash'])] + cmd_postfix
|
||||||
|
|
||||||
dummy, out, dummy = module.run_command(openssl_sign_cmd, data=sign_payload, check_rc=True, binary_data=True)
|
dummy, out, dummy = module.run_command(openssl_sign_cmd, data=sign_payload, check_rc=True, binary_data=True)
|
||||||
|
|
||||||
if key_data['type'] == 'ec':
|
if key_data['type'] == 'ec':
|
||||||
|
@ -403,9 +437,43 @@ def _parse_key_cryptography(module, key_file=None, key_content=None):
|
||||||
return 'unknown key type "{0}"'.format(type(key)), {}
|
return 'unknown key type "{0}"'.format(type(key)), {}
|
||||||
|
|
||||||
|
|
||||||
|
def _create_mac_key_cryptography(module, alg, key):
|
||||||
|
if alg == 'HS256':
|
||||||
|
hashalg = cryptography.hazmat.primitives.hashes.SHA256
|
||||||
|
hashbytes = 32
|
||||||
|
elif alg == 'HS384':
|
||||||
|
hashalg = cryptography.hazmat.primitives.hashes.SHA384
|
||||||
|
hashbytes = 48
|
||||||
|
elif alg == 'HS512':
|
||||||
|
hashalg = cryptography.hazmat.primitives.hashes.SHA512
|
||||||
|
hashbytes = 64
|
||||||
|
else:
|
||||||
|
raise ModuleFailException('Unsupported MAC key algorithm for cryptography backend: {0}'.format(alg))
|
||||||
|
key_bytes = base64.urlsafe_b64decode(key)
|
||||||
|
if len(key_bytes) < hashbytes:
|
||||||
|
raise ModuleFailException(
|
||||||
|
'{0} key must be at least {1} bytes long (after Base64 decoding)'.format(alg, hashbytes))
|
||||||
|
return {
|
||||||
|
'mac_obj': lambda: cryptography.hazmat.primitives.hmac.HMAC(
|
||||||
|
key_bytes,
|
||||||
|
hashalg(),
|
||||||
|
_cryptography_backend),
|
||||||
|
'type': 'hmac',
|
||||||
|
'alg': alg,
|
||||||
|
'jwk': {
|
||||||
|
'kty': 'oct',
|
||||||
|
'k': key,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _sign_request_cryptography(module, payload64, protected64, key_data):
|
def _sign_request_cryptography(module, payload64, protected64, key_data):
|
||||||
sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
|
sign_payload = "{0}.{1}".format(protected64, payload64).encode('utf8')
|
||||||
if isinstance(key_data['key_obj'], cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
|
if 'mac_obj' in key_data:
|
||||||
|
mac = key_data['mac_obj']()
|
||||||
|
mac.update(sign_payload)
|
||||||
|
signature = mac.finalize()
|
||||||
|
elif isinstance(key_data['key_obj'], cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
|
||||||
padding = cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15()
|
padding = cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15()
|
||||||
hashalg = cryptography.hazmat.primitives.hashes.SHA256
|
hashalg = cryptography.hazmat.primitives.hashes.SHA256
|
||||||
signature = key_data['key_obj'].sign(sign_payload, padding, hashalg())
|
signature = key_data['key_obj'].sign(sign_payload, padding, hashalg())
|
||||||
|
@ -556,6 +624,13 @@ class ACMEAccount(object):
|
||||||
else:
|
else:
|
||||||
return _sign_request_openssl(self._openssl_bin, self.module, payload64, protected64, key_data)
|
return _sign_request_openssl(self._openssl_bin, self.module, payload64, protected64, key_data)
|
||||||
|
|
||||||
|
def _create_mac_key(self, alg, key):
|
||||||
|
'''Create a MAC key.'''
|
||||||
|
if HAS_CURRENT_CRYPTOGRAPHY:
|
||||||
|
return _create_mac_key_cryptography(self.module, alg, key)
|
||||||
|
else:
|
||||||
|
return _create_mac_key_openssl(self._openssl_bin, self.module, alg, key)
|
||||||
|
|
||||||
def _log(self, msg, data=None):
|
def _log(self, msg, data=None):
|
||||||
'''
|
'''
|
||||||
Write arguments to acme.log when logging is enabled.
|
Write arguments to acme.log when logging is enabled.
|
||||||
|
@ -683,13 +758,19 @@ class ACMEAccount(object):
|
||||||
self.jws_header.pop('jwk')
|
self.jws_header.pop('jwk')
|
||||||
self.jws_header['kid'] = self.uri
|
self.jws_header['kid'] = self.uri
|
||||||
|
|
||||||
def _new_reg(self, contact=None, agreement=None, terms_agreed=False, allow_creation=True):
|
def _new_reg(self, contact=None, agreement=None, terms_agreed=False, allow_creation=True,
|
||||||
|
external_account_binding=None):
|
||||||
'''
|
'''
|
||||||
Registers a new ACME account. Returns a pair ``(created, data)``.
|
Registers a new ACME account. Returns a pair ``(created, data)``.
|
||||||
Here, ``created`` is ``True`` if the account was created and
|
Here, ``created`` is ``True`` if the account was created and
|
||||||
``False`` if it already existed (e.g. it was not newly created),
|
``False`` if it already existed (e.g. it was not newly created),
|
||||||
or does not exist. In case the account was created or exists,
|
or does not exist. In case the account was created or exists,
|
||||||
``data`` contains the account data; otherwise, it is ``None``.
|
``data`` contains the account data; otherwise, it is ``None``.
|
||||||
|
|
||||||
|
If specified, ``external_account_binding`` should be a dictionary
|
||||||
|
with keys ``kid``, ``alg`` and ``key``
|
||||||
|
(https://tools.ietf.org/html/rfc8555#section-7.3.4).
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.3
|
https://tools.ietf.org/html/rfc8555#section-7.3
|
||||||
'''
|
'''
|
||||||
contact = contact or []
|
contact = contact or []
|
||||||
|
@ -703,8 +784,23 @@ class ACMEAccount(object):
|
||||||
new_reg['agreement'] = agreement
|
new_reg['agreement'] = agreement
|
||||||
else:
|
else:
|
||||||
new_reg['agreement'] = self.directory['meta']['terms-of-service']
|
new_reg['agreement'] = self.directory['meta']['terms-of-service']
|
||||||
|
if external_account_binding is not None:
|
||||||
|
raise ModuleFailException('External account binding is not supported for ACME v1')
|
||||||
url = self.directory['new-reg']
|
url = self.directory['new-reg']
|
||||||
else:
|
else:
|
||||||
|
if (external_account_binding is not None or self.directory['meta'].get('externalAccountRequired')) and allow_creation:
|
||||||
|
# Some ACME servers such as ZeroSSL do not like it when you try to register an existing account
|
||||||
|
# and provide external_account_binding credentials. Thus we first send a request with allow_creation=False
|
||||||
|
# to see whether the account already exists.
|
||||||
|
|
||||||
|
# Note that we pass contact here: ZeroSSL does not accept regisration calls without contacts, even
|
||||||
|
# if onlyReturnExisting is set to true.
|
||||||
|
created, data = self._new_reg(contact=contact, allow_creation=False)
|
||||||
|
if data:
|
||||||
|
# An account already exists! Return data
|
||||||
|
return created, data
|
||||||
|
# An account does not yet exist. Try to create one next.
|
||||||
|
|
||||||
new_reg = {
|
new_reg = {
|
||||||
'contact': contact
|
'contact': contact
|
||||||
}
|
}
|
||||||
|
@ -714,6 +810,21 @@ class ACMEAccount(object):
|
||||||
if terms_agreed:
|
if terms_agreed:
|
||||||
new_reg['termsOfServiceAgreed'] = True
|
new_reg['termsOfServiceAgreed'] = True
|
||||||
url = self.directory['newAccount']
|
url = self.directory['newAccount']
|
||||||
|
if external_account_binding is not None:
|
||||||
|
new_reg['externalAccountBinding'] = self.sign_request(
|
||||||
|
{
|
||||||
|
'alg': external_account_binding['alg'],
|
||||||
|
'kid': external_account_binding['kid'],
|
||||||
|
'url': url,
|
||||||
|
},
|
||||||
|
self.jwk,
|
||||||
|
self._create_mac_key(external_account_binding['alg'], external_account_binding['key'])
|
||||||
|
)
|
||||||
|
elif self.directory['meta'].get('externalAccountRequired') and allow_creation:
|
||||||
|
raise ModuleFailException(
|
||||||
|
'To create an account, an external account binding must be specified. '
|
||||||
|
'Use the acme_account module with the external_account_binding option.'
|
||||||
|
)
|
||||||
|
|
||||||
result, info = self.send_signed_request(url, new_reg)
|
result, info = self.send_signed_request(url, new_reg)
|
||||||
|
|
||||||
|
@ -783,7 +894,9 @@ class ACMEAccount(object):
|
||||||
raise ModuleFailException("Error getting account data from {2}: {0} {1}".format(info['status'], result, self.uri))
|
raise ModuleFailException("Error getting account data from {2}: {0} {1}".format(info['status'], result, self.uri))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def setup_account(self, contact=None, agreement=None, terms_agreed=False, allow_creation=True, remove_account_uri_if_not_exists=False):
|
def setup_account(self, contact=None, agreement=None, terms_agreed=False,
|
||||||
|
allow_creation=True, remove_account_uri_if_not_exists=False,
|
||||||
|
external_account_binding=None):
|
||||||
'''
|
'''
|
||||||
Detect or create an account on the ACME server. For ACME v1,
|
Detect or create an account on the ACME server. For ACME v1,
|
||||||
as the only way (without knowing an account URI) to test if an
|
as the only way (without knowing an account URI) to test if an
|
||||||
|
@ -803,6 +916,10 @@ class ACMEAccount(object):
|
||||||
The account URI will be stored in ``self.uri``; if it is ``None``,
|
The account URI will be stored in ``self.uri``; if it is ``None``,
|
||||||
the account does not exist.
|
the account does not exist.
|
||||||
|
|
||||||
|
If specified, ``external_account_binding`` should be a dictionary
|
||||||
|
with keys ``kid``, ``alg`` and ``key``
|
||||||
|
(https://tools.ietf.org/html/rfc8555#section-7.3.4).
|
||||||
|
|
||||||
https://tools.ietf.org/html/rfc8555#section-7.3
|
https://tools.ietf.org/html/rfc8555#section-7.3
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -821,7 +938,8 @@ class ACMEAccount(object):
|
||||||
contact,
|
contact,
|
||||||
agreement=agreement,
|
agreement=agreement,
|
||||||
terms_agreed=terms_agreed,
|
terms_agreed=terms_agreed,
|
||||||
allow_creation=allow_creation and not self.module.check_mode
|
allow_creation=allow_creation and not self.module.check_mode,
|
||||||
|
external_account_binding=external_account_binding,
|
||||||
)
|
)
|
||||||
if self.module.check_mode and self.uri is None and allow_creation:
|
if self.module.check_mode and self.uri is None and allow_creation:
|
||||||
created = True
|
created = True
|
||||||
|
|
|
@ -86,6 +86,33 @@ options:
|
||||||
- "Mutually exclusive with C(new_account_key_src)."
|
- "Mutually exclusive with C(new_account_key_src)."
|
||||||
- "Required if C(new_account_key_src) is not used and state is C(changed_key)."
|
- "Required if C(new_account_key_src) is not used and state is C(changed_key)."
|
||||||
type: str
|
type: str
|
||||||
|
external_account_binding:
|
||||||
|
description:
|
||||||
|
- Allows to provide external account binding data during account creation.
|
||||||
|
- This is used by CAs like Sectigo to bind a new ACME account to an existing CA-specific
|
||||||
|
account, to be able to properly identify a customer.
|
||||||
|
- Only used when creating a new account. Can not be specified for ACME v1.
|
||||||
|
type: dict
|
||||||
|
suboptions:
|
||||||
|
kid:
|
||||||
|
description:
|
||||||
|
- The key identifier provided by the CA.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
alg:
|
||||||
|
description:
|
||||||
|
- The MAC algorithm provided by the CA.
|
||||||
|
- If not specified by the CA, this is probably C(HS256).
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
choices: [ HS256, HS384, HS512 ]
|
||||||
|
key:
|
||||||
|
description:
|
||||||
|
- Base64 URL encoded value of the MAC key provided by the CA.
|
||||||
|
- Padding (C(=) symbols at the end) can be omitted.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
version_added: 1.1.0
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
@ -125,6 +152,8 @@ account_uri:
|
||||||
type: str
|
type: str
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import base64
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
from ansible_collections.community.crypto.plugins.module_utils.acme import (
|
from ansible_collections.community.crypto.plugins.module_utils.acme import (
|
||||||
|
@ -144,6 +173,11 @@ def main():
|
||||||
contact=dict(type='list', elements='str', default=[]),
|
contact=dict(type='list', elements='str', default=[]),
|
||||||
new_account_key_src=dict(type='path'),
|
new_account_key_src=dict(type='path'),
|
||||||
new_account_key_content=dict(type='str', no_log=True),
|
new_account_key_content=dict(type='str', no_log=True),
|
||||||
|
external_account_binding=dict(type='dict', options=dict(
|
||||||
|
kid=dict(type='str', required=True),
|
||||||
|
alg=dict(type='str', required=True, choices=['HS256', 'HS384', 'HS512']),
|
||||||
|
key=dict(type='str', required=True, no_log=True),
|
||||||
|
))
|
||||||
))
|
))
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
|
@ -163,6 +197,18 @@ def main():
|
||||||
)
|
)
|
||||||
handle_standard_module_arguments(module, needs_acme_v2=True)
|
handle_standard_module_arguments(module, needs_acme_v2=True)
|
||||||
|
|
||||||
|
if module.params['external_account_binding']:
|
||||||
|
# Make sure padding is there
|
||||||
|
key = module.params['external_account_binding']['key']
|
||||||
|
if len(key) % 4 != 0:
|
||||||
|
key = key + ('=' * (4 - (len(key) % 4)))
|
||||||
|
# Make sure key is Base64 encoded
|
||||||
|
try:
|
||||||
|
base64.urlsafe_b64decode(key)
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg='Key for external_account_binding must be Base64 URL encoded (%s)' % e)
|
||||||
|
module.params['external_account_binding']['key'] = key
|
||||||
|
|
||||||
try:
|
try:
|
||||||
account = ACMEAccount(module)
|
account = ACMEAccount(module)
|
||||||
changed = False
|
changed = False
|
||||||
|
@ -189,13 +235,14 @@ def main():
|
||||||
changed = True
|
changed = True
|
||||||
elif state == 'present':
|
elif state == 'present':
|
||||||
allow_creation = module.params.get('allow_creation')
|
allow_creation = module.params.get('allow_creation')
|
||||||
# Make sure contact is a list of strings (unfortunately, Ansible doesn't do that for us)
|
|
||||||
contact = [str(v) for v in module.params.get('contact')]
|
contact = [str(v) for v in module.params.get('contact')]
|
||||||
terms_agreed = module.params.get('terms_agreed')
|
terms_agreed = module.params.get('terms_agreed')
|
||||||
|
external_account_binding = module.params.get('external_account_binding')
|
||||||
created, account_data = account.setup_account(
|
created, account_data = account.setup_account(
|
||||||
contact,
|
contact,
|
||||||
terms_agreed=terms_agreed,
|
terms_agreed=terms_agreed,
|
||||||
allow_creation=allow_creation,
|
allow_creation=allow_creation,
|
||||||
|
external_account_binding=external_account_binding,
|
||||||
)
|
)
|
||||||
if account_data is None:
|
if account_data is None:
|
||||||
raise ModuleFailException(msg='Account does not exist or is deactivated.')
|
raise ModuleFailException(msg='Account does not exist or is deactivated.')
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
- name: Generate account key
|
- name: Generate account keys
|
||||||
command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/accountkey.pem
|
command: openssl ecparam -name prime256v1 -genkey -out {{ output_dir }}/{{ item }}.pem
|
||||||
|
loop:
|
||||||
|
- accountkey
|
||||||
|
- accountkey2
|
||||||
|
- accountkey3
|
||||||
|
- accountkey4
|
||||||
|
- accountkey5
|
||||||
|
|
||||||
- name: Parse account key (to ease debugging some test failures)
|
- name: Parse account keys (to ease debugging some test failures)
|
||||||
command: openssl ec -in {{ output_dir }}/accountkey.pem -noout -text
|
command: openssl ec -in {{ output_dir }}/{{ item }}.pem -noout -text
|
||||||
|
loop:
|
||||||
|
- accountkey
|
||||||
|
- accountkey2
|
||||||
|
- accountkey3
|
||||||
|
- accountkey4
|
||||||
|
- accountkey5
|
||||||
|
|
||||||
- name: Do not try to create account
|
- name: Do not try to create account
|
||||||
acme_account:
|
acme_account:
|
||||||
|
@ -153,12 +165,6 @@
|
||||||
contact: []
|
contact: []
|
||||||
register: account_modified_2_idempotent
|
register: account_modified_2_idempotent
|
||||||
|
|
||||||
- name: Generate new account key
|
|
||||||
command: openssl ecparam -name secp384r1 -genkey -out {{ output_dir }}/accountkey2.pem
|
|
||||||
|
|
||||||
- name: Parse account key (to ease debugging some test failures)
|
|
||||||
command: openssl ec -in {{ output_dir }}/accountkey2.pem -noout -text
|
|
||||||
|
|
||||||
- name: Change account key (check mode, diff)
|
- name: Change account key (check mode, diff)
|
||||||
acme_account:
|
acme_account:
|
||||||
select_crypto_backend: "{{ select_crypto_backend }}"
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
@ -242,3 +248,36 @@
|
||||||
allow_creation: no
|
allow_creation: no
|
||||||
ignore_errors: yes
|
ignore_errors: yes
|
||||||
register: account_not_created_3
|
register: account_not_created_3
|
||||||
|
|
||||||
|
- name: Create account with External Account Binding
|
||||||
|
acme_account:
|
||||||
|
select_crypto_backend: "{{ select_crypto_backend }}"
|
||||||
|
account_key_src: "{{ output_dir }}/{{ item.account }}.pem"
|
||||||
|
acme_version: 2
|
||||||
|
acme_directory: https://{{ acme_host }}:14000/dir
|
||||||
|
validate_certs: no
|
||||||
|
state: present
|
||||||
|
allow_creation: yes
|
||||||
|
terms_agreed: yes
|
||||||
|
contact:
|
||||||
|
- mailto:example@example.org
|
||||||
|
external_account_binding:
|
||||||
|
kid: "{{ item.kid }}"
|
||||||
|
alg: "{{ item.alg }}"
|
||||||
|
key: "{{ item.key }}"
|
||||||
|
register: account_created_eab
|
||||||
|
ignore_errors: yes
|
||||||
|
loop:
|
||||||
|
- account: accountkey3
|
||||||
|
kid: kid-1
|
||||||
|
alg: HS256
|
||||||
|
key: zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W
|
||||||
|
- account: accountkey4
|
||||||
|
kid: kid-2
|
||||||
|
alg: HS384
|
||||||
|
key: b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH
|
||||||
|
- account: accountkey5
|
||||||
|
kid: kid-3
|
||||||
|
alg: HS512
|
||||||
|
key: zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W
|
||||||
|
- debug: var=account_created_eab
|
||||||
|
|
|
@ -127,3 +127,11 @@
|
||||||
that:
|
that:
|
||||||
- account_not_created_3 is failed
|
- account_not_created_3 is failed
|
||||||
- account_not_created_3.msg == 'Account does not exist or is deactivated.'
|
- account_not_created_3.msg == 'Account does not exist or is deactivated.'
|
||||||
|
|
||||||
|
- name: Validate that the account with External Account Binding has been created
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- account_created_eab.results[0] is changed
|
||||||
|
- account_created_eab.results[1] is changed
|
||||||
|
- account_created_eab.results[2] is failed
|
||||||
|
- "'HS512 key must be at least 64 bytes long' in account_created_eab.results[2].msg"
|
||||||
|
|
Loading…
Reference in New Issue