diff --git a/lib/ansible/module_utils/ipa.py b/lib/ansible/module_utils/ipa.py new file mode 100644 index 00000000000..ccf580c564e --- /dev/null +++ b/lib/ansible/module_utils/ipa.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c) 2016 Thomas Krahn (@Nosmoht) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +try: + import json +except ImportError: + import simplejson as json + +from ansible.module_utils.pycompat24 import get_exception +from ansible.module_utils.urls import fetch_url +from ansible.module_utils.six.moves.urllib.parse import quote +from ansible.module_utils.six import PY3 +from ansible.module_utils._text import to_bytes, to_text + +class IPAClient(object): + def __init__(self, module, host, port, protocol): + self.host = host + self.port = port + self.protocol = protocol + self.module = module + self.headers = None + + def get_base_url(self): + return '%s://%s/ipa' % (self.protocol, self.host) + + def get_json_url(self): + return '%s/session/json' % self.get_base_url() + + def login(self, username, password): + url = '%s/session/login_password' % self.get_base_url() + data = 'user=%s&password=%s' % (quote(username, safe=''), quote(password, safe='')) + headers = {'referer': self.get_base_url(), + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'text/plain'} + try: + resp, info = fetch_url(module=self.module, url=url, data=to_bytes(data), headers=headers) + status_code = info['status'] + if status_code not in [200, 201, 204]: + self._fail('login', info['msg']) + + self.headers = {'referer': self.get_base_url(), + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Cookie': resp.info().get('Set-Cookie')} + except Exception: + e = get_exception() + self._fail('login', str(e)) + + def _fail(self, msg, e): + if 'message' in e: + err_string = e.get('message') + else: + err_string = e + self.module.fail_json(msg='%s: %s' % (msg, err_string)) + + def _post_json(self, method, name, item=None): + if item is None: + item = {} + url = '%s/session/json' % self.get_base_url() + data = {'method': method, 'params': [[name], item]} + try: + resp, info = fetch_url(module=self.module, url=url, data=to_bytes(json.dumps(data)), headers=self.headers) + status_code = info['status'] + if status_code not in [200, 201, 204]: + self._fail(method, info['msg']) + except Exception: + e = get_exception() + self._fail('post %s' % method, str(e)) + + if PY3: + charset = resp.headers.get_content_charset('latin-1') + else: + response_charset = resp.headers.getparam('charset') + if response_charset: + charset = response_charset + else: + charset = 'latin-1' + resp = json.loads(to_text(resp.read(), encoding=charset), encoding=charset) + err = resp.get('error') + if err is not None: + self._fail('repsonse %s' % method, err) + + if 'result' in resp: + result = resp.get('result') + if 'result' in result: + result = result.get('result') + if isinstance(result, list): + if len(result) > 0: + return result[0] + else: + return {} + return result + return None