ozwillo: add synchronization script (#30027)

This commit is contained in:
Benjamin Dauvergne 2019-01-23 12:59:26 +01:00
parent 7aa49fcf12
commit 050dd5dbd6
1 changed files with 149 additions and 0 deletions

View File

@ -0,0 +1,149 @@
# Ozwillo plugin to deploy Publik
# Copyright (C) 2017-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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 or 'accounts.sictiam.fr' 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)
if 'Location' not in br.response.headers:
logger.warning(u'Not authorization form %r %r', response.status_code, response._content[:1000])
return
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()
if '-tsl.' in tenant.get_base_url():
print json.dump(response.json(), indent=2)
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)