From 99bfe2146b633b963769b07027826ae475e2d185 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 7 Aug 2017 17:00:53 +0200 Subject: [PATCH] ozwillo: add script for synchronization of ozwillo users It needs robobrowser to be installed using pip in /usr/local. You need to add the following line in a crontab (every 20 minutes as a baseline): authentic2-multitenant-manage runscript hobo.contrib.ozwillo.scripts.synchronize_ozwillo_users --- hobo/contrib/ozwillo/scripts/__init__.py | 0 .../ozwillo/scripts/synchronize_ozwillo_users.py | 127 +++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 hobo/contrib/ozwillo/scripts/__init__.py create mode 100644 hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py diff --git a/hobo/contrib/ozwillo/scripts/__init__.py b/hobo/contrib/ozwillo/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py b/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py new file mode 100644 index 0000000..570ee84 --- /dev/null +++ b/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py @@ -0,0 +1,127 @@ +import json +import datetime +import logging +import urlparse +from urllib import urlencode +import pprint +import os + +import requests +from robobrowser.browser import RoboBrowser + +from hobo.multitenant.middleware import TenantMiddleware +from tenant_schemas.utils import tenant_context + + +def run_on_all_tenants(f): + for tenant in TenantMiddleware.get_tenants(): + with tenant_context(tenant): + try: + f(tenant) + except: + logging.exception('unable to provision') + +def provision_users(tenant): + from django.conf import settings + + if not getattr(settings, 'OZWILLO_ADMIN', None): + logging.warning('No OZWILLO_ADMIN setting found') + return + + logger = logging.getLogger('ozwillo_synchro') + from authentic2_auth_oidc.models import OIDCProvider + for provider in OIDCProvider.objects.all(): + if 'ozwillo' in provider.issuer: + auth_url = provider.authorization_endpoint + token_url = provider.token_endpoint + client_id = provider.client_id + client_secret = provider.client_secret + redirect_uri = urlparse.urljoin(tenant.get_base_url(), '/accounts/oidc/callback/') + instance_users_url = urlparse.urljoin(token_url.replace('accounts', 'kernel'), '/apps/acl/instance/') + break + else: + logger.warning('No ozwillo OIDC provider found for %s', tenant.get_base_url()) + return + + session = requests.Session() + session.verify = False + + br = RoboBrowser(user_agent='Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0', + session=session) + + query = urlencode({ + 'client_id': client_id, + 'response_type': 'code', + 'scope': 'openid offline_access', + 'prompt': 'consent', + 'redirect_uri': redirect_uri + }) + response = br.open(auth_url + '?%s' % query) + response = br._states[-1].response + if br._states[-1].response.status_code != 200: + logger.warning(u'OIDC authorization request failed: %s %r', response.status_code, response._content[:1000]) + return + referer = br.response.request.url + br.session.headers['Referer'] = referer + form = br.get_form(action='/a/login') + form['u'] = settings.OZWILLO_ADMIN[0] + form['pwd'] = settings.OZWILLO_ADMIN[1] + br.submit_form(form=form) + referer = br.response.request.url + br.session.headers['Referer'] = referer + form = br.get_form() + br.submit_form(form=form, allow_redirects=False) + + cb_url = urlparse.urlparse(br.response.headers['location']) + + code = urlparse.parse_qs(cb_url.query)['code'][0] + + logger.info('Getting token from %s', token_url) + response = requests.post(token_url, auth=(client_id, client_secret), + data={ + 'code': code, + 'redirect_uri': redirect_uri, + 'grant_type': 'authorization_code'}) + logger.info('Got %s', response.json()) + access_token = response.json()['access_token'] + logger.info('Getting instance users from %s', instance_users_url + client_id) + response = requests.get(instance_users_url + client_id, + headers={'Authorization': 'Bearer %s' % access_token}) + logger.info('Got %s', response.json()) + + from django.contrib.auth import get_user_model + from authentic2_auth_oidc.models import OIDCAccount + + User = get_user_model() + + for user in response.json(): + logging.info('Provisionning email %s with sub %s', user['user_email_address'], user['user_id']) + while True: + new_user = User.objects.create() + oidc_account, created = OIDCAccount.objects.select_related().get_or_create(provider=provider, sub=user['user_id'], defaults={'user': new_user}) + if created: + if OIDCAccount.objects.filter(provider=provider, sub=user['user_id']).count() > 1: + oidc_account.delete() + new_user.delete() + continue + logging.info('provisionned with uuid %s', new_user.uuid) + break + else: + new_user.delete() + new_user = oidc_account.user + break + save = False + if new_user.username != user['user_name']: + new_user.username = user['user_name'] + save = True + if new_user.email != user['user_email_address']: + new_user.email = user['user_email_address'] + save = True + if new_user.ou != provider.ou: + new_user.ou = provider.ou + save = True + if save: + new_user.save() + + +run_on_all_tenants(provision_users) -- 2.1.4