Open l00ptr opened 2 years ago
Hello, it is a good idea. We already tried this for W'Sweet, but code is not yet published.
ok, could you share this code ? I would like those features (it could be easier than writing them from scratch)
Here is an ansible module written by @maxbes to create OIDC RP through API:
#!/usr/bin/python
# Copyright: (c) 2018, Terry Jones <terry.jones@example.org>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: lemonldap_oidc_rp
short_description: This module creates a new OIDC Relying Party in LemonLDAP
version_added: "2.8"
description:
- "This module uses the Manager API to create a new OIDC Relying Party"
options:
name:
description:
- The arbitrary, internal name of this party in LemonLDAP::NG
required: true
state:
description:
- Whether the party should exist or not, taking action if the state is different from what is stated
type: str
choices: [ absent, present ]
default: present
options:
description:
- Dictionnary of OIDC Relying Party options
type: dict
clientId=dict(type='str', required=False),
clientId:
description:
- The OpenID Connect Client ID of this party
redirectUris:
description:
- The list of allowed redirect URIs for this party
type: list
exported_variables:
description:
- Dictionnary of LemonLDAP::NG session attributes to send to this party
- The key of each dictionnary is the name of the OIDC claim
- The value is the name of LemonLDAP session attribute to use for this claim
type: dict
extra_claims:
description:
- Dictionary of additional scopes you want this party to support
- The key of each dictionnary is the name of a scope
- The value is the space-separated list of OIDC claims released by this scope
type: dict
api_url:
description:
- URL of the LemonLDAP::NG Manager API
type: str
required: true
url_username:
description:
- Username to login on the Manager API as
type: str
url_password:
description:
- Password for the Manager API
type: str
timeout:
description:
- Request timeout for the Manager API
type: int
default: 60
author:
- Your Name (@yourhandle)
'''
EXAMPLE = '''
# Create a new OIDC Relying party
- name: create
lemonldap_oidc_rp:
api_url: https://manager-api.dev.wsweet.cloud/
url_username: manager
url_password: password
name: myopenidapp
state: present
clientId: myclientid
options:
clientSecret: myclientsecret
redirectUris:
- https://myapp.dev.wsweet.cloud/oauth2/callback
exported_variables:
email: mail
family_name: sn
given_name: givenName
preferred_username: uid
'''
RETURN = '''
original_message:
description: The original name param that was passed in
type: str
returned: always
message:
description: The output message that the test module generates
type: str
returned: always
'''
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.lemonldap_api import LemonApi,lemon_base_spec
class OidcRpApi(LemonApi):
def get_object(self):
name = self.module.params['name']
url = "/providers/oidc/rp/{0}".format(name)
return self.api_request(url)
def differs(self, current_object):
if self.module.params['clientId'] != current_object['clientId']:
return True
if self._differs_hash(current_object, 'options', 'options'):
return True
if self._differs_hash(current_object, 'exported_variables', 'exportedVars'):
return True
if self._differs_hash(current_object, 'extra_claims', 'extraClaims'):
return True
return False
def _differs_hash(self, current_object, option_name, payload_key):
specification = self.module.params[option_name]
state = current_object[payload_key]
if specification is None:
return False
for keyname in specification.keys():
if not keyname in state or specification[keyname] != state[keyname]:
return True
def build_payload(self,confKey = None):
data = {}
if not confKey is None:
data['confKey'] = confKey
if 'clientId' in self.module.params:
data['clientId'] = self.module.params['clientId']
if 'redirectUris' in self.module.params:
data['redirectUris'] = self.module.params['redirectUris']
if 'options' in self.module.params:
data['options'] = self.module.params['options']
if 'exported_variables' in self.module.params:
data['exportedVars'] = self.module.params['exported_variables']
if 'extra_claims' in self.module.params:
data['extraClaims'] = self.module.params['extra_claims']
return data
def create(self):
url = "/providers/oidc/rp"
data = self.build_payload(self.module.params['name'])
self.api_request(url, method='POST', data=data, expect=201)
self.module.exit_json(changed=True)
def update(self):
url = "/providers/oidc/rp/{0}".format(self.module.params['name'])
data = self.build_payload()
self.api_request(url, method='PATCH', data=data, expect=204)
self.module.exit_json(changed=True)
def delete(self):
url = "/providers/oidc/rp/{0}".format(self.module.params['name'])
self.api_request(url, method='DELETE', expect=204)
self.module.exit_json(changed=True)
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = lemon_base_spec(dict(
name=dict(type='str', required=True),
clientId=dict(type='str', required=False),
redirectUris=dict(type='list', elements='str', required=False,),
options=dict(type='dict', required=False,),
exported_variables=dict(type='dict', required=False,),
extra_claims=dict(type='dict', required=False,),
state=dict(type='str', default="present", choices=["present", "absent"]),
))
# seed the result dict in the object
# we primarily care about changed and state
# change is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
)
# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
api = OidcRpApi(module)
api.run()
def main():
run_module()
if __name__ == '__main__':
main()
great thx for sharing this piece of code
Hello,
It could be nice to be able to adapt the config of a running LemonLDAP through the API within ansible. I know there is an API for the manager, but don't know if we have enough features there to create an Ansible module for this purpose. What do you think ? It could be fun to explore and create a first PoC with some very simple object (menucat or menuapp). If you think that's good idea, i will spend some time the few next weeks exploring this.
Best regards, l.