You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
decort-ansible/library/decort_jwt.py

158 lines
6.3 KiB

#!/usr/bin/python
#
# Digital Enegry Cloud Orchestration Technology (DECORT) modules for Ansible
# Copyright: (c) 2018-2021 Digital Energy Cloud Solutions LLC
#
# Apache License 2.0 (see http://www.apache.org/licenses/LICENSE-2.0.txt)
#
#
# Author: Sergey Shubin (sergey.shubin@digitalenergy.online)
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: decort_jwt
short_description: Obtain access token to be used for authentication to DECORT cloud controller
description:
- Obtain JWT (JSON Web Token) from the specified Oauth2 provider. This JWT can be used in subsequent DECS modules'
invocations to authenticate them to the DECS cloud controller.
version_added: "2.4"
author: "Sergey Shubin (sergey.shubin@digitalenergy.online)"
notes:
- Environment variables can be used to pass parameters to the module (see options below for details).
- Specified Oauth2 provider must be trusted by the DECORT cloud controller on which JWT will be used.
- 'If you register module output as I(my_jwt), the JWT value is accessed as I(my_jwt.jwt)'
requirements:
- python >= 2.6
- PyJWT module
- requests module
- decort_utils utility library (module)
- DECORT cloud platform version 3.6.1 or higher
options:
app_id:
description:
- 'Application ID for authenticating to the Oauth2 provider specified in I(oauth2_url).'
- 'If not found in the playbook or command line arguments, the value will be taken from DECORT_APP_ID
environment variable.'
required: no
app_secret:
description:
- 'Application API secret used for authenticating to the Oauth2 provider specified in I(oauth2_url).'
- 'If not found in the playbook or command line arguments, the value will be taken from DECORT_APP_SECRET
environment variable.'
required: no
oauth2_url:
description:
- 'URL of the oauth2 authentication provider to obtain JWT from.'
- If not specified in the playbook, the value will be taken from DECORT_OAUTH2_URL environment variable.
validity:
description:
- Validity of the JWT in seconds. Default value is 3600 (one hour).
required: no
verify_ssl:
description:
- 'Controls SSL verification mode when making API calls to DECS controller. Set it to False if you
want to disable SSL certificate verification.'
- `Intended use case is when you run module in a trusted environment that uses self-signed certificates.
Note that disabling SSL verification in any other scenario can lead to security issues, so please use
with caution.'
default: True
required: no
'''
EXAMPLES = '''
- name: Obtain JWT and store it as my_jwt for authenticating subsequent task to DECORT cloud controller
decort_jwt:
app_id: "{{ my_app_id }}"
app_secret: "{{ my_app_secret }}"
oauth2_url: https://sso.decs.online
delegate_to: localhost
register: my_jwt
'''
RETURN = '''
jwt:
description: JSON Web Token that can be used to access DECS cloud controller
returned: always
type: string
sample: None
'''
import requests
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
def decort_jwt_parameters():
"""Build and return a dictionary of parameters expected by decort_jwt module in a form accepted
by AnsibleModule utility class"""
return dict(
app_id=dict(type='str',
required=True,
fallback=(env_fallback, ['DECORT_APP_ID'])),
app_secret=dict(type='str',
required=True,
fallback=(env_fallback, ['DECORT_APP_SECRET']),
no_log=True),
oauth2_url=dict(type='str',
required=True,
fallback=(env_fallback, ['DECORT_OAUTH2_URL'])),
validity=dict(type='int',
required=False,
default=3600),
verify_ssl=dict(type='bool', required=False, default=True),
workflow_callback=dict(type='str', required=False),
workflow_context=dict(type='str', required=False),
)
def main():
module_parameters = decort_jwt_parameters()
amodule = AnsibleModule(argument_spec=module_parameters,
supports_check_mode=True,)
result = {'failed': False, 'changed': False}
token_get_url = amodule.params['oauth2_url'] + "/v1/oauth/access_token"
req_data = dict(grant_type="client_credentials",
client_id=amodule.params['app_id'],
client_secret=amodule.params['app_secret'],
response_type="id_token",
validity=amodule.params['validity'],)
# TODO: Need standard code snippet to handle server timeouts gracefully
# Consider a few retries before giving up or use requests.Session & requests.HTTPAdapter
# see https://stackoverflow.com/questions/15431044/can-i-set-max-retries-for-requests-request
# catch requests.exceptions.ConnectionError to handle incorrect oauth2_url case
try:
token_get_resp = requests.post(token_get_url, data=req_data, verify=amodule.params['verify_ssl'])
except requests.exceptions.ConnectionError as errco:
result.update(failed=True)
result['msg'] = "Failed to connect to {}: {}".format(token_get_url, errco)
amodule.fail_json(**result)
except requests.exceptions.Timeout as errti:
result.update(failed=True)
result['msg'] = "Timeout when trying to connect to {}: {}".format(token_get_url, errti)
amodule.fail_json(**result)
# alternative -- if resp == requests.codes.ok
if token_get_resp.status_code != 200:
result.update(failed=True)
result['msg'] = "Failed to obtain JWT access token from oauth2_url {} for app_id {}: {} {}".format(
token_get_url, amodule.params['app_id'],
token_get_resp.status_code, token_get_resp.reason)
amodule.fail_json(**result)
# Common return values: https://docs.ansible.com/ansible/2.3/common_return_values.html
result['jwt'] = token_get_resp.content.decode('utf8')
amodule.exit_json(**result)
if __name__ == '__main__':
main()