mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add keycloak_realm_key module (#7127)
* Add keycloak_realm_key module * keycloak_realm_key: make "ansible-test sanity" happy Signed-off-by: Samuli Seppänen <samuli.seppanen@puppeteers.net> * keycloak_realm_key: support check_mode * keycloak_realm_key: add integration tests * keycloak_realm_key: remove FIXME comment * keycloak_realm_key: fix EOL in integration test variables * keycloak_realm_key: remove unused import * keycloak_realm_key: remove integration test realm at the end of test suite * keycloak_realm_key: add version_added metadata * keycloak_realm_key: add documentation for end_state * keycloak_realm_key: support the "certificate" parameter As with "private_key" changing the certificate after creation is not possible because we can't compare the current value to the desired value. * keycloak_realm_key: document default for certificate parameter Signed-off-by: Samuli Seppänen <samuli.seppanen@puppeteers.net> * keycloak_realm_key: implement diff mode * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * keycloak_realm_key: remove note about literal linefeeds * keycloak_realm_key: remove defaults from priority and certificate * keycloak_realm_key: mark diff and check modes as partially supported * keycloak_realm_key: implement "force" parameter This ensures that the desired state is always enforced on keys that should be, and are, present. * keycloak_realm_key: fix yaml parsing error in documentation * keycloak_realm_key: document why check_mode support is partial * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> * keycloak_realm_key: documentation and metadata fixes * keycloak_realm_key: small documentation fix * keycloak_realm_key: change version_added to 7.5.0 * Update plugins/modules/keycloak_realm_key.py Co-authored-by: Felix Fontein <felix@fontein.de> --------- Signed-off-by: Samuli Seppänen <samuli.seppanen@puppeteers.net> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
721108d92e
commit
9a7a7a9658
6 changed files with 912 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -725,6 +725,8 @@ files:
|
|||
maintainers: kris2kris
|
||||
$modules/keycloak_realm_info.py:
|
||||
maintainers: fynncfchen
|
||||
$modules/keycloak_realm_key.py:
|
||||
maintainers: mattock
|
||||
$modules/keycloak_role.py:
|
||||
maintainers: laurpaum
|
||||
$modules/keycloak_user.py:
|
||||
|
|
457
plugins/modules/keycloak_realm_key.py
Normal file
457
plugins/modules/keycloak_realm_key.py
Normal file
|
@ -0,0 +1,457 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2017, Eike Frost <ei@kefro.st>
|
||||
# Copyright (c) 2021, Christophe Gilles <christophe.gilles54@gmail.com>
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
|
||||
# https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: keycloak_realm_key
|
||||
|
||||
short_description: Allows administration of Keycloak realm keys via Keycloak API
|
||||
|
||||
version_added: 7.5.0
|
||||
|
||||
description:
|
||||
- This module allows the administration of Keycloak realm keys via the Keycloak REST API. It
|
||||
requires access to the REST API via OpenID Connect; the user connecting and the realm being
|
||||
used must have the requisite access rights. In a default Keycloak installation, admin-cli
|
||||
and an admin user would work, as would a separate realm definition with the scope tailored
|
||||
to your needs and a user having the expected roles.
|
||||
|
||||
- The names of module options are snake_cased versions of the camelCase ones found in the
|
||||
Keycloak API and its documentation at U(https://www.keycloak.org/docs-api/8.0/rest-api/index.html).
|
||||
Aliases are provided so camelCased versions can be used as well.
|
||||
|
||||
- This module is unable to detect changes to the actual cryptographic key after importing it.
|
||||
However, if some other property is changed alongside the cryptographic key, then the key
|
||||
will also get changed as a side-effect, as the JSON payload needs to include the private key.
|
||||
This can be considered either a bug or a feature, as the alternative would be to always
|
||||
update the realm key whether it has changed or not.
|
||||
|
||||
- If certificate is not explicitly provided it will be dynamically created by Keycloak.
|
||||
Therefore comparing the current state of the certificate to the desired state (which may be
|
||||
empty) is not possible.
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: partial
|
||||
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- State of the keycloak realm key.
|
||||
- On V(present), the realm key will be created (or updated if it exists already).
|
||||
- On V(absent), the realm key will be removed if it exists.
|
||||
choices: ['present', 'absent']
|
||||
default: 'present'
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- Name of the realm key to create.
|
||||
type: str
|
||||
required: true
|
||||
force:
|
||||
description:
|
||||
- Enforce the state of the private key and certificate. This is not automatically the
|
||||
case as this module is unable to determine the current state of the private key and
|
||||
thus cannot trigger an update based on an actual divergence. That said, a private key
|
||||
update may happen even if force is false as a side-effect of other changes.
|
||||
default: false
|
||||
type: bool
|
||||
parent_id:
|
||||
description:
|
||||
- The parent_id of the realm key. In practice the ID (name) of the realm.
|
||||
type: str
|
||||
required: true
|
||||
provider_id:
|
||||
description:
|
||||
- The name of the "provider ID" for the key.
|
||||
choices: ['rsa']
|
||||
default: 'rsa'
|
||||
type: str
|
||||
config:
|
||||
description:
|
||||
- Dict specifying the key and its properties.
|
||||
type: dict
|
||||
suboptions:
|
||||
active:
|
||||
description:
|
||||
- Whether they key is active or inactive. Not to be confused with the state
|
||||
of the Ansible resource managed by the O(state) parameter.
|
||||
default: true
|
||||
type: bool
|
||||
enabled:
|
||||
description:
|
||||
- Whether the key is enabled or disabled. Not to be confused with the state
|
||||
of the Ansible resource managed by the O(state) parameter.
|
||||
default: true
|
||||
type: bool
|
||||
priority:
|
||||
description:
|
||||
- The priority of the key.
|
||||
type: int
|
||||
required: true
|
||||
algorithm:
|
||||
description:
|
||||
- Key algorithm.
|
||||
default: RS256
|
||||
choices: ['RS256']
|
||||
type: str
|
||||
private_key:
|
||||
description:
|
||||
- The private key as an ASCII string. Contents of the key must match O(config.algorithm)
|
||||
and O(provider_id).
|
||||
- Please note that the module cannot detect whether the private key specified differs from the
|
||||
current state's private key. Use O(force=true) to force the module to update the private key
|
||||
if you expect it to be updated.
|
||||
required: true
|
||||
type: str
|
||||
certificate:
|
||||
description:
|
||||
- A certificate signed with the private key as an ASCII string. Contents of the
|
||||
key must match O(config.algorithm) and O(provider_id).
|
||||
- If you want Keycloak to automatically generate a certificate using your private key
|
||||
then set this to an empty string.
|
||||
required: true
|
||||
type: str
|
||||
notes:
|
||||
- Current value of the private key cannot be fetched from Keycloak.
|
||||
Therefore comparing its desired state to the current state is not
|
||||
possible.
|
||||
- If certificate is not explicitly provided it will be dynamically created
|
||||
by Keycloak. Therefore comparing the current state of the certificate to
|
||||
the desired state (which may be empty) is not possible.
|
||||
- Due to the private key and certificate options the module is
|
||||
B(not fully idempotent). You can use O(force=true) to force the module
|
||||
to always update if you know that the private key might have changed.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.keycloak
|
||||
- community.general.attributes
|
||||
|
||||
author:
|
||||
- Samuli Seppänen (@mattock)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Manage Keycloak realm key (certificate autogenerated by Keycloak)
|
||||
community.general.keycloak_realm_key:
|
||||
name: custom
|
||||
state: present
|
||||
parent_id: master
|
||||
provider_id: rsa
|
||||
auth_keycloak_url: http://localhost:8080/auth
|
||||
auth_username: keycloak
|
||||
auth_password: keycloak
|
||||
auth_realm: master
|
||||
config:
|
||||
private_key: "{{ private_key }}"
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 120
|
||||
algorithm: RS256
|
||||
- name: Manage Keycloak realm key and certificate
|
||||
community.general.keycloak_realm_key:
|
||||
name: custom
|
||||
state: present
|
||||
parent_id: master
|
||||
provider_id: rsa
|
||||
auth_keycloak_url: http://localhost:8080/auth
|
||||
auth_username: keycloak
|
||||
auth_password: keycloak
|
||||
auth_realm: master
|
||||
config:
|
||||
private_key: "{{ private_key }}"
|
||||
certificate: "{{ certificate }}"
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 120
|
||||
algorithm: RS256
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: Message as to what action was taken.
|
||||
returned: always
|
||||
type: str
|
||||
|
||||
end_state:
|
||||
description: Representation of the keycloak_realm_key after module execution.
|
||||
returned: on success
|
||||
type: dict
|
||||
contains:
|
||||
id:
|
||||
description: ID of the realm key.
|
||||
type: str
|
||||
returned: when O(state=present)
|
||||
sample: 5b7ec13f-99da-46ad-8326-ab4c73cf4ce4
|
||||
name:
|
||||
description: Name of the realm key.
|
||||
type: str
|
||||
returned: when O(state=present)
|
||||
sample: mykey
|
||||
parentId:
|
||||
description: ID of the realm this key belongs to.
|
||||
type: str
|
||||
returned: when O(state=present)
|
||||
sample: myrealm
|
||||
providerId:
|
||||
description: The ID of the key provider.
|
||||
type: str
|
||||
returned: when O(state=present)
|
||||
sample: rsa
|
||||
providerType:
|
||||
description: The type of provider.
|
||||
type: str
|
||||
returned: when O(state=present)
|
||||
config:
|
||||
description: Realm key configuration.
|
||||
type: dict
|
||||
returned: when O(state=present)
|
||||
sample: {
|
||||
"active": ["true"],
|
||||
"algorithm": ["RS256"],
|
||||
"enabled": ["true"],
|
||||
"priority": ["140"]
|
||||
}
|
||||
'''
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import KeycloakAPI, camel, \
|
||||
keycloak_argument_spec, get_token, KeycloakError
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Module execution
|
||||
|
||||
:return:
|
||||
"""
|
||||
argument_spec = keycloak_argument_spec()
|
||||
|
||||
meta_args = dict(
|
||||
state=dict(type='str', default='present', choices=['present', 'absent']),
|
||||
name=dict(type='str', required=True),
|
||||
force=dict(type='bool', default=False),
|
||||
parent_id=dict(type='str', required=True),
|
||||
provider_id=dict(type='str', default='rsa', choices=['rsa']),
|
||||
config=dict(
|
||||
type='dict',
|
||||
options=dict(
|
||||
active=dict(type='bool', default=True),
|
||||
enabled=dict(type='bool', default=True),
|
||||
priority=dict(type='int', required=True),
|
||||
algorithm=dict(type='str', default='RS256', choices=['RS256']),
|
||||
private_key=dict(type='str', required=True, no_log=True),
|
||||
certificate=dict(type='str', required=True, no_log=True)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
argument_spec.update(meta_args)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=([['token', 'auth_realm', 'auth_username', 'auth_password']]),
|
||||
required_together=([['auth_realm', 'auth_username', 'auth_password']]))
|
||||
|
||||
# Initialize the result object. Only "changed" seems to have special
|
||||
# meaning for Ansible.
|
||||
result = dict(changed=False, msg='', end_state={}, diff=dict(before={}, after={}))
|
||||
|
||||
# This will include the current state of the realm key if it is already
|
||||
# present. This is only used for diff-mode.
|
||||
before_realm_key = {}
|
||||
before_realm_key['config'] = {}
|
||||
|
||||
# Obtain access token, initialize API
|
||||
try:
|
||||
connection_header = get_token(module.params)
|
||||
except KeycloakError as e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
kc = KeycloakAPI(module, connection_header)
|
||||
|
||||
params_to_ignore = list(keycloak_argument_spec().keys()) + ["state", "force"]
|
||||
|
||||
# Filter and map the parameters names that apply to the role
|
||||
component_params = [x for x in module.params
|
||||
if x not in params_to_ignore and
|
||||
module.params.get(x) is not None]
|
||||
|
||||
# We only support one component provider type in this module
|
||||
provider_type = 'org.keycloak.keys.KeyProvider'
|
||||
|
||||
# Build a proposed changeset from parameters given to this module
|
||||
changeset = {}
|
||||
changeset['config'] = {}
|
||||
|
||||
# Generate a JSON payload for Keycloak Admin API from the module
|
||||
# parameters. Parameters that do not belong to the JSON payload (e.g.
|
||||
# "state" or "auth_keycloal_url") have been filtered away earlier (see
|
||||
# above).
|
||||
#
|
||||
# This loop converts Ansible module parameters (snake-case) into
|
||||
# Keycloak-compatible format (camel-case). For example private_key
|
||||
# becomes privateKey.
|
||||
#
|
||||
# It also converts bool, str and int parameters into lists with a single
|
||||
# entry of 'str' type. Bool values are also lowercased. This is required
|
||||
# by Keycloak.
|
||||
#
|
||||
for component_param in component_params:
|
||||
if component_param == 'config':
|
||||
for config_param in module.params.get('config'):
|
||||
changeset['config'][camel(config_param)] = []
|
||||
raw_value = module.params.get('config')[config_param]
|
||||
if isinstance(raw_value, bool):
|
||||
value = str(raw_value).lower()
|
||||
else:
|
||||
value = str(raw_value)
|
||||
|
||||
changeset['config'][camel(config_param)].append(value)
|
||||
else:
|
||||
# No need for camelcase in here as these are one word parameters
|
||||
new_param_value = module.params.get(component_param)
|
||||
changeset[camel(component_param)] = new_param_value
|
||||
|
||||
# As provider_type is not a module parameter we have to add it to the
|
||||
# changeset explicitly.
|
||||
changeset['providerType'] = provider_type
|
||||
|
||||
# Make a deep copy of the changeset. This is use when determining
|
||||
# changes to the current state.
|
||||
changeset_copy = deepcopy(changeset)
|
||||
|
||||
# It is not possible to compare current keys to desired keys, because the
|
||||
# certificate parameter is a base64-encoded binary blob created on the fly
|
||||
# when a key is added. Moreover, the Keycloak Admin API does not seem to
|
||||
# return the value of the private key for comparison. So, in effect, it we
|
||||
# just have to ignore changes to the keys. However, as the privateKey
|
||||
# parameter needs be present in the JSON payload, any changes done to any
|
||||
# other parameters (e.g. config.priority) will trigger update of the keys
|
||||
# as a side-effect.
|
||||
del changeset_copy['config']['privateKey']
|
||||
del changeset_copy['config']['certificate']
|
||||
|
||||
# Make it easier to refer to current module parameters
|
||||
name = module.params.get('name')
|
||||
force = module.params.get('force')
|
||||
state = module.params.get('state')
|
||||
enabled = module.params.get('enabled')
|
||||
provider_id = module.params.get('provider_id')
|
||||
parent_id = module.params.get('parent_id')
|
||||
|
||||
# Get a list of all Keycloak components that are of keyprovider type.
|
||||
realm_keys = kc.get_components(urlencode(dict(type=provider_type, parent=parent_id)), parent_id)
|
||||
|
||||
# If this component is present get its key ID. Confusingly the key ID is
|
||||
# also known as the Provider ID.
|
||||
key_id = None
|
||||
|
||||
# Track individual parameter changes
|
||||
changes = ""
|
||||
|
||||
# This tells Ansible whether the key was changed (added, removed, modified)
|
||||
result['changed'] = False
|
||||
|
||||
# Loop through the list of components. If we encounter a component whose
|
||||
# name matches the value of the name parameter then assume the key is
|
||||
# already present.
|
||||
for key in realm_keys:
|
||||
if key['name'] == name:
|
||||
key_id = key['id']
|
||||
changeset['id'] = key_id
|
||||
changeset_copy['id'] = key_id
|
||||
|
||||
# Compare top-level parameters
|
||||
for param, value in changeset.items():
|
||||
before_realm_key[param] = key[param]
|
||||
|
||||
if changeset_copy[param] != key[param] and param != 'config':
|
||||
changes += "%s: %s -> %s, " % (param, key[param], changeset_copy[param])
|
||||
result['changed'] = True
|
||||
|
||||
# Compare parameters under the "config" key
|
||||
for p, v in changeset_copy['config'].items():
|
||||
before_realm_key['config'][p] = key['config'][p]
|
||||
if changeset_copy['config'][p] != key['config'][p]:
|
||||
changes += "config.%s: %s -> %s, " % (p, key['config'][p], changeset_copy['config'][p])
|
||||
result['changed'] = True
|
||||
|
||||
# Sanitize linefeeds for the privateKey. Without this the JSON payload
|
||||
# will be invalid.
|
||||
changeset['config']['privateKey'][0] = changeset['config']['privateKey'][0].replace('\\n', '\n')
|
||||
changeset['config']['certificate'][0] = changeset['config']['certificate'][0].replace('\\n', '\n')
|
||||
|
||||
# Check all the possible states of the resource and do what is needed to
|
||||
# converge current state with desired state (create, update or delete
|
||||
# the key).
|
||||
if key_id and state == 'present':
|
||||
if result['changed']:
|
||||
if module._diff:
|
||||
del before_realm_key['config']['privateKey']
|
||||
del before_realm_key['config']['certificate']
|
||||
result['diff'] = dict(before=before_realm_key, after=changeset_copy)
|
||||
|
||||
if module.check_mode:
|
||||
result['msg'] = "Realm key %s would be changed: %s" % (name, changes.strip(", "))
|
||||
else:
|
||||
kc.update_component(changeset, parent_id)
|
||||
result['msg'] = "Realm key %s changed: %s" % (name, changes.strip(", "))
|
||||
elif not result['changed'] and force:
|
||||
kc.update_component(changeset, parent_id)
|
||||
result['changed'] = True
|
||||
result['msg'] = "Realm key %s was forcibly updated" % (name)
|
||||
else:
|
||||
result['msg'] = "Realm key %s was in sync" % (name)
|
||||
|
||||
result['end_state'] = changeset_copy
|
||||
elif key_id and state == 'absent':
|
||||
if module._diff:
|
||||
del before_realm_key['config']['privateKey']
|
||||
del before_realm_key['config']['certificate']
|
||||
result['diff'] = dict(before=before_realm_key, after={})
|
||||
|
||||
if module.check_mode:
|
||||
result['changed'] = True
|
||||
result['msg'] = "Realm key %s would be deleted" % (name)
|
||||
else:
|
||||
kc.delete_component(key_id, parent_id)
|
||||
result['changed'] = True
|
||||
result['msg'] = "Realm key %s deleted" % (name)
|
||||
|
||||
result['end_state'] = {}
|
||||
elif not key_id and state == 'present':
|
||||
if module._diff:
|
||||
result['diff'] = dict(before={}, after=changeset_copy)
|
||||
|
||||
if module.check_mode:
|
||||
result['changed'] = True
|
||||
result['msg'] = "Realm key %s would be created" % (name)
|
||||
else:
|
||||
kc.create_component(changeset, parent_id)
|
||||
result['changed'] = True
|
||||
result['msg'] = "Realm key %s created" % (name)
|
||||
|
||||
result['end_state'] = changeset_copy
|
||||
elif not key_id and state == 'absent':
|
||||
result['changed'] = False
|
||||
result['msg'] = "Realm key %s not present" % (name)
|
||||
result['end_state'] = {}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
5
tests/integration/targets/keycloak_realm_key/aliases
Normal file
5
tests/integration/targets/keycloak_realm_key/aliases
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
unsupported
|
27
tests/integration/targets/keycloak_realm_key/readme.adoc
Normal file
27
tests/integration/targets/keycloak_realm_key/readme.adoc
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Ansible Project
|
||||
// GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
To be able to run these integration tests a keycloak server must be
|
||||
reachable under a specific url with a specific admin user and password.
|
||||
The exact values expected for these parameters can be found in
|
||||
'vars/main.yml' file. A simple way to do this is to use the official
|
||||
keycloak docker images like this:
|
||||
|
||||
----
|
||||
docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=<url-path> -e KEYCLOAK_ADMIN=<admin_user> -e KEYCLOAK_ADMIN_PASSWORD=<admin_password> quay.io/keycloak/keycloak:20.0.2 start-dev
|
||||
----
|
||||
|
||||
Example with concrete values inserted:
|
||||
|
||||
----
|
||||
docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:20.0.2 start-dev
|
||||
----
|
||||
|
||||
This test suite can run against a fresh unconfigured server instance
|
||||
(no preconfiguration required) and cleans up after itself (undoes all
|
||||
its config changes) as long as it runs through completly. While its active
|
||||
it changes the server configuration in the following ways:
|
||||
|
||||
* creating, modifying and deleting some keycloak groups
|
||||
|
373
tests/integration/targets/keycloak_realm_key/tasks/main.yml
Normal file
373
tests/integration/targets/keycloak_realm_key/tasks/main.yml
Normal file
|
@ -0,0 +1,373 @@
|
|||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
- name: Remove Keycloak test realm to avoid failures from previous failed runs
|
||||
community.general.keycloak_realm:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
realm: "{{ realm }}"
|
||||
id: "{{ realm }}"
|
||||
state: absent
|
||||
|
||||
- name: Create Keycloak test realm
|
||||
community.general.keycloak_realm:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
realm: "{{ realm }}"
|
||||
id: "{{ realm }}"
|
||||
state: present
|
||||
|
||||
- name: Create custom realm key (check mode)
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 150
|
||||
check_mode: true
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["150"]
|
||||
- result.msg == "Realm key testkey would be created"
|
||||
|
||||
- name: Create custom realm key
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 150
|
||||
diff: true
|
||||
register: result
|
||||
|
||||
- name: Assert that realm key was created
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["150"]
|
||||
- result.msg == "Realm key testkey created"
|
||||
|
||||
- name: Create custom realm key (test for idempotency)
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 150
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["150"]
|
||||
- result.msg == "Realm key testkey was in sync"
|
||||
|
||||
- name: Update custom realm key (check mode)
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 140
|
||||
check_mode: true
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["140"]
|
||||
- result.msg == "Realm key testkey would be changed: config.priority ['150'] -> ['140']"
|
||||
|
||||
- name: Update custom realm key
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 140
|
||||
diff: true
|
||||
register: result
|
||||
|
||||
- name: Assert that realm key was updated
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["140"]
|
||||
- result.msg == "Realm key testkey changed: config.priority ['150'] -> ['140']"
|
||||
|
||||
- name: Update custom realm key (test for idempotency)
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 140
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["140"]
|
||||
- result.msg == "Realm key testkey was in sync"
|
||||
|
||||
- name: Force update custom realm key
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
force: true
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key_2 }}"
|
||||
certificate: ""
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 140
|
||||
register: result
|
||||
|
||||
- name: Assert that forced update ran correctly
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["140"]
|
||||
- result.msg == "Realm key testkey was forcibly updated"
|
||||
|
||||
- name: Remove custom realm key
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: absent
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
priority: 140
|
||||
diff: true
|
||||
register: result
|
||||
|
||||
- name: Assert that realm key was deleted
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state == {}
|
||||
- result.msg == "Realm key testkey deleted"
|
||||
|
||||
- name: Remove custom realm key (test for idempotency)
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey
|
||||
state: absent
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: ""
|
||||
priority: 140
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.end_state == {}
|
||||
- result.msg == "Realm key testkey not present"
|
||||
|
||||
- name: Create custom realm key with a custom certificate
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey_with_certificate
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "{{ realm_private_key }}"
|
||||
certificate: "{{ realm_certificate }}"
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 150
|
||||
diff: true
|
||||
register: result
|
||||
|
||||
- name: Assert that realm key with custom certificate was created
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey_with_certificate"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["150"]
|
||||
- result.msg == "Realm key testkey_with_certificate created"
|
||||
|
||||
- name: Attempt to change the private key and the certificate
|
||||
community.general.keycloak_realm_key:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
name: testkey_with_certificate
|
||||
state: present
|
||||
parent_id: "{{ realm }}"
|
||||
config:
|
||||
private_key: "a different private key string"
|
||||
certificate: "a different certificate string"
|
||||
enabled: true
|
||||
active: true
|
||||
priority: 150
|
||||
diff: true
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing has changed
|
||||
assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.end_state != {}
|
||||
- result.end_state.name == "testkey_with_certificate"
|
||||
- result.end_state.parentId == "realm_key_test"
|
||||
- result.end_state.providerId == "rsa"
|
||||
- result.end_state.providerType == "org.keycloak.keys.KeyProvider"
|
||||
- result.end_state.config.active == ["true"]
|
||||
- result.end_state.config.enabled == ["true"]
|
||||
- result.end_state.config.algorithm == ["RS256"]
|
||||
- result.end_state.config.priority == ["150"]
|
||||
- result.msg == "Realm key testkey_with_certificate was in sync"
|
||||
|
||||
- name: Remove Keycloak test realm
|
||||
community.general.keycloak_realm:
|
||||
auth_keycloak_url: "{{ url }}"
|
||||
auth_realm: "{{ admin_realm }}"
|
||||
auth_username: "{{ admin_user }}"
|
||||
auth_password: "{{ admin_password }}"
|
||||
realm: "{{ realm }}"
|
||||
id: "{{ realm }}"
|
||||
state: absent
|
48
tests/integration/targets/keycloak_realm_key/vars/main.yml
Normal file
48
tests/integration/targets/keycloak_realm_key/vars/main.yml
Normal file
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
url: http://localhost:8080/auth
|
||||
admin_realm: master
|
||||
admin_user: admin
|
||||
admin_password: password
|
||||
realm: realm_key_test
|
||||
realm_private_key_name: testkey
|
||||
realm_private_key: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC9Mi7IKXPhqGiWGwgEYEXnqc8nytG1pHbC6QYZe1gUa43jDtGYQln36It02BGw4e5XydCUj+M26X2sH+kKaV+KHEnJtcEqdAuVX1QaDVzeiOoo1/B9HC8By6NZBsOSdxpat3EvilQ+R7NP9yi53J08+vfeZSEGyPmKV1me7nJnRh3/zcRsOi92GTsBd7gApKfP8sorDjY8m9NRuPLwleK2nh/aRvj1yK8x3UAqUIbOCVaE39bSN6VUTFK2Q/+MX3vF0Zugsk7PKKmfqcEW6wj7dtSElbX4uhrfTkGMmwIWdIiLDNRA/jVRvGxUB1SyMy6kmMC8jC2QGWpZgfkSKtHlAgMBAAECggEACWkSVh7ntmjtwM+Z47vVJkt2NBS8vxPt206DYOeXbzaVUV6mkrP0LSZKL3bi1GE8fW3am9UXWF8fQt04dm3c1G4JRojtkXrBq72Y3Y3eGWyGdx8chWCOPwDdwFsbhbC6ZRo8PUDcZVekJd1Vj38XbBXQl+WAUcnTzauAF+1kz9mhJq1gpglIbB+8l7VjMXwXeaGWJQ5OL/MSsq7r3P1elVjHwprFBM7HHA5+RTu/KY/GcEutgm5uwTRqRZNC1IBXAQtBO7HQJbuLqDPTQ3RRCPEur8R+0dk5bF+8IyzQ8Bh+Dhuou9xzfS/A7lV6L/CZSpv4Bvq1H3Uxk+orXf2Q2QKBgQDBOf1nSJB0VgQdIcdtgVpVgQ2SyWAd+N8Qk7QsyVQf9f7ZqiFLejWJbaaeY9WtfZ01D8tgHJfPqsO1/Jux255mtkyk2K2c6dav1Lsd4l+iPfidsDJNWkcd59nQqwC9BLjzWK/J4rO20apm34abLaZ9oVk8Mgz8VWJWOxTgCr+COQKBgQD6qP1lm6rzlCSIEz9eCuGPkQkVo+NIP437e3i+sxtkLlMgnmfzSwSJdVF8AKH3gXi3NyWjfBVYeAZEkm1kHF8IWOiK4U1y95Vx3uud3NX4SC+cjePc+pDPQJiz9L+zq9I6WFZWmm7n/9heTxu/l0vxI4FHaBmt95BMwLJNkzbdDQKBgCHGwUUMqjOr1YxCG1pJAkFwDa9bBDI5DsUXDKfHia0Mkz/5PVi0RCeBw15slS1+h7x+xk5GsULb1to5Df5JJadOtpcaST7koWKbDRpsN8tkidEGu8RJw6S2opyXR8nCyZHALvpbZo7Ol7rj1+PIVxIe4jpjhWGWi1oHed6wAkoBAoGAJx2F5XxEUhx1EvMF+XPzPQciBsl7Z0PbsTnUXtXuWVTNThLKH/I99AFlxNcIb2o530VwzzFG13Zra/n5rhyrS88sArgj8OPn40wpMopKraL+Iw0VWN+VB3KKIdL4s14FwWsVlhAlbHjFV/o6V0yR4kBrJSx+jWJLl16etHJbpmUCgYBUWCQwcT1aw9XHWJXiNYTnQSYg88hgGYhts1qSzhfu+n1t2BlAlxM0gu2+gez21mM8uiYsqbU2OZeG2U4as6kdai8Q4tzNQt2f1r3ZewJN/QHrkx6FT94PNa0w4ILiQ9Eu7xssaHcYjHyrI1NlbMKypVy6waDG2ajLOFAVeHGpOg==
|
||||
-----END PRIVATE KEY-----
|
||||
realm_certificate: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAiigAwIBAgIUMfPlHWcZn6xfeSjfbhgmt4yy6mMwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yMzA4MTgxMTU5MDFaFw0zMzA4MTUxMTU5MDFaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Mi7IKXPhqGiWGwgEYEXnqc8nytG1pHbC6QYZe1gUa43jDtGYQln36It02BGw4e5XydCUj+M26X2sH+kKaV+KHEnJtcEqdAuVX1QaDVzeiOoo1/B9HC8By6NZBsOSdxpat3EvilQ+R7NP9yi53J08+vfeZSEGyPmKV1me7nJnRh3/zcRsOi92GTsBd7gApKfP8sorDjY8m9NRuPLwleK2nh/aRvj1yK8x3UAqUIbOCVaE39bSN6VUTFK2Q/+MX3vF0Zugsk7PKKmfqcEW6wj7dtSElbX4uhrfTkGMmwIWdIiLDNRA/jVRvGxUB1SyMy6kmMC8jC2QGWpZgfkSKtHlAgMBAAGjLjAsMAsGA1UdDwQEAwIEkDAdBgNVHQ4EFgQUcZirWRV5EzRhanUVSQ9rmAavVbEwDQYJKoZIhvcNAQELBQADggEBAIt2aFr/sxvtZfDc+Nb9tgspBuoX8f9Gf9mrS6dTdvdqSMHQrcoejSEEAZNljdSpKAhnhyR3+uCIev++WS4tixZoooQ8aYxDGNIwyry51GNEK7LKXVRmkbZFODidRuYZ1XWQORaJoaXWplaPaNtLvUr1swachz36K4n8/UIi109w/addajOHFbFGAzUmGRR4saMZPGrQCaNFje7G1o5wb/mQD1L+Jfk81Id5/F6NFBsSEIi+/O7Xs7fOWuab6cdfwI7zQQclEo55WQkLXefFLn+Ju0Ftgl023awpNEE4pjl6jD5VSEOkQ+I2sxGvymgjz7Av4zPOD/Lr05lRnMxf8dA=
|
||||
-----END CERTIFICATE-----
|
||||
realm_private_key_2: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyQ5FKuqbnWEtt
|
||||
KI0FHKFvd+G/RyEI2ow29Ytjs3fZ7/gMYfXozHLLJl3jgCOvSf9Ta55arL0XnCCf
|
||||
RKQb0vpgMmOTQw++A1UmNXe8atTczZMRiHMHFdLhXUvUKthcMGOrTH8xegCnm0bG
|
||||
rZwimjQDog/kROMAN78Uv8SD1lMpGBxPr2DWXNl4kRF670m/jC0cM7SeDGCCKVF5
|
||||
SEh6rMDgI62AxKnbtxuAbF9SOO/6kTsYv5+dc8wxDEb0aaT1jC1CLhjAVmjc6vO7
|
||||
WHE0LLas+ARs4ghMONLN6VdOkJxBuEtlLqM3M+/viD1TRftZCeLarYLWdEsg8Yz9
|
||||
Ufb0oawzAgMBAAECggEARqPDxWsljHNOoFj7WNU5m6RTzqpvCsUf3v96Vu3dRn1z
|
||||
O+Ttv2yU6K+xcN9sRJ/8D6CLxb7Bx8NUoghfR69ZDBmrn8VpTZCgg12Yrw9efojw
|
||||
CHibrGkXgbqou9CmoBGEzXKozIBeFgzQBRby0jts9SuZRImPspxkmeJMCzo5BgUg
|
||||
ksNibaWikvUJYMgFc7PdXEvxhCKcWTTGC3fxJwpRxXkqKsYDa3JhdhloH8hHqynm
|
||||
o7WEXeGAn4UV7C1tg3OdTciHn/ONMRItPcyonwk19meZTvsEub6ZsNjVg/5oJVBr
|
||||
WG8vPZBi1VzAMayDXxDOnEAKW5eJXRSNX1vZ7EQTEQKBgQDXg5pSp9hVdVZc+eN/
|
||||
Ab/1NMMdgrQdbyTeB9esjLiwNuXysQm/KaG8gTkLpiKVvJ8R7SOcxb9Y5Gt9Y5Ej
|
||||
eu943V4zLDIzNt/ST4bXGW/gQ84zkMBdhKz9hKA5tartVjI1ycznjpDbgn/jAYPI
|
||||
8VXGmjID2oDIJ7P+dLD8lMBDvQKBgQDTwIyimy+4EwFUuuppfWArXRsqsWUScGWD
|
||||
+06xbc+Ld92LJBvakvSTdDNnS/PlYGl/fJjqQ4wq5UPREJYCi3UW9I5jtfsIg8Pl
|
||||
oCnIhEYkn8xPZ7X8grU4emkM6QAPhstCDlXE6t0T202TpYVYjtEEDRQu4rKAbJ0h
|
||||
gqSh5Ge2rwKBgEjrx6jWEBYCaOF20ComTmxKmQaANi+Lbt8NqkVBLDC7spymmJSt
|
||||
IoOk+cdeRG+D7hLjuVwPcQpD57b6nJ5zt1mfFYOdHbNEiwEfVZGskrVAXCIIhX5f
|
||||
KSVy3cAJHzfFJaIbkRB8pbkQc/M8jPnN5ucXP3scUNzoyjd8BnLAZjnFAoGAWwwY
|
||||
rDYTz48EbH0uG4uYFS0kaDf8YHBJhfVBgdLYgXxZmuE8xL+ZP+mfzJOA3CiXVASr
|
||||
71Z551vKzBLYnWF/SA6BRuhRdvjI+2vha2FMk6TOAXpzao59AzrG/pEUwJhRvyZQ
|
||||
xKnDwyzxb0GlU02dG6PQANTisYuCCI2W4jFGUusCgYB72p5o5uBr7qrFMTdMMxxe
|
||||
f/9Go/9QBR/uNYk3D/rWj0F/bXGbiYMddNMD4v3XE24NL4ZvBJn0Po64Tuz5+wtu
|
||||
5ICKc6ED1l55MPsKdegVMpXGIFRjZt2TtCk4FE68m5QJpT1IIK7I9jv0+FGKjFYa
|
||||
ukdTEghu13cANd8eKpxBsQ==
|
||||
-----END PRIVATE KEY-----
|
Loading…
Reference in a new issue