From 5e865ad9b311575360e8a470d779df1c2693505b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sun, 18 Apr 2021 20:33:35 +0200 Subject: [PATCH] general: remove ozwillo extension (#53229) --- MANIFEST.in | 3 - hobo/contrib/ozwillo/README.rst | 105 ------- hobo/contrib/ozwillo/__init__.py | 0 hobo/contrib/ozwillo/admin.py | 10 - .../ozwillo/examples/import-site-agents.json | 30 -- .../examples/import-site-template.json | 169 ----------- .../ozwillo/examples/template_recipe.json | 67 ---- hobo/contrib/ozwillo/management/__init__.py | 0 .../ozwillo/management/commands/__init__.py | 0 .../management/commands/ozwillo_worker.py | 68 ----- .../ozwillo/migrations/0001_initial.py | 21 -- .../migrations/0002_auto_20180517_1047.py | 52 ---- .../migrations/0003_auto_20180524_0828.py | 20 -- hobo/contrib/ozwillo/migrations/__init__.py | 0 hobo/contrib/ozwillo/models.py | 285 ------------------ .../ozwillo/scripts/create_user_ozwillo.py | 55 ---- .../scripts/synchronize_ozwillo_users.py | 148 --------- hobo/contrib/ozwillo/urls.py | 25 -- hobo/contrib/ozwillo/views.py | 144 --------- hobo/urls.py | 5 - tests/settings.py | 3 +- 21 files changed, 1 insertion(+), 1209 deletions(-) delete mode 100644 hobo/contrib/ozwillo/README.rst delete mode 100644 hobo/contrib/ozwillo/__init__.py delete mode 100644 hobo/contrib/ozwillo/admin.py delete mode 100644 hobo/contrib/ozwillo/examples/import-site-agents.json delete mode 100644 hobo/contrib/ozwillo/examples/import-site-template.json delete mode 100644 hobo/contrib/ozwillo/examples/template_recipe.json delete mode 100644 hobo/contrib/ozwillo/management/__init__.py delete mode 100644 hobo/contrib/ozwillo/management/commands/__init__.py delete mode 100644 hobo/contrib/ozwillo/management/commands/ozwillo_worker.py delete mode 100644 hobo/contrib/ozwillo/migrations/0001_initial.py delete mode 100644 hobo/contrib/ozwillo/migrations/0002_auto_20180517_1047.py delete mode 100644 hobo/contrib/ozwillo/migrations/0003_auto_20180524_0828.py delete mode 100644 hobo/contrib/ozwillo/migrations/__init__.py delete mode 100644 hobo/contrib/ozwillo/models.py delete mode 100644 hobo/contrib/ozwillo/scripts/create_user_ozwillo.py delete mode 100644 hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py delete mode 100644 hobo/contrib/ozwillo/urls.py delete mode 100644 hobo/contrib/ozwillo/views.py diff --git a/MANIFEST.in b/MANIFEST.in index 3b98a56..acc0b2e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -13,9 +13,6 @@ recursive-include hobo/locale *.po *.mo recursive-include hobo/environment/locale *.po *.mo recursive-include hobo/agent/authentic2/locale *.po *.mo recursive-include tests *.py *.json -recursive-include hobo/contrib/ozwillo/scripts *.py -recursive-include hobo/contrib/ozwillo/examples *.json -include hobo/contrib/ozwillo/README.rst include hobo/multitenant/README include MANIFEST.in include COPYING diff --git a/hobo/contrib/ozwillo/README.rst b/hobo/contrib/ozwillo/README.rst deleted file mode 100644 index 0abc2a6..0000000 --- a/hobo/contrib/ozwillo/README.rst +++ /dev/null @@ -1,105 +0,0 @@ -Ozwillo contrib app for SICTIAM -=============================== - -Install on Debian ------------------ - -1. add `hobo.contrib.ozwillo` to INSTALLED_APPS - -2. copy files from examples/ into /etc/hobo/ozwillo/ (must be readable by all) - -3. copy following line in /etc/sudoers.d/sictiam:: - - hobo ALL=(ALL:ALL) NOPASSWD: ALL - -4. set the following variables in `/etc/hobo/settings.d/10_ozwillo.py`: - - - OZWILLO_SECRET - - OZWILLO_ENV_DOMAIN (e.g: sictiam.dev.entrouvert.org) - - OZWILLO_DESTRUCTION_URI - - OZWILLO_DESTRUCTION_SECRET - - OZWILLO_PLATEFORM (https://dev.entrouvert.org/projects/sictiam/wiki/Raccordement_OpenID_Connect_%C3%A0_Ozwillo for the values) - - OZWILLO_SERVICES (use only for the destruction, explained down) - - Exemple:: - - OZWILLO_DESTRUCTION_SECRET = "mysecret" - OZWILLO_DESTRUCTION_URI = "https://hobo-sve.test-demarches.sictiam.fr/ozwillo/delete-publik-instance/" - OZWILLO_ENABLED = True, - OZWILLO_ENV_DOMAIN = "test-demarches.sictiam.fr" - OZWILLO_PLATEFORM = "https://accounts.ozwillo-preprod.eu/" - OZWILLO_SECRET = "myothersecret" - OZWILLO_SERVICES = { - "authentic-multitenant": [ - "connexion-", - "authentic2-multitenant-manage" - ], - "combo_agent": [ - "agents-", - "combo-manage" - ], - "combo_usager": [ - "", - "combo-manage" - ], - "fargo": [ - "porte-documents-", - "fargo-manage" - ], - "hobo": [ - "hobo-", - "hobo-manage" - ], - "passerelle": [ - "passerelle-", - "passerelle-manage" - ], - "wcs": [ - "demarches-", - "wcsctl" - ] - } - -Design ------- - -The views create-publik-instance receive an ozwillo request with some clients -informations (secret and id), the ozwillo user sending the request, the -organization name (which is the collectivity's name to deploy) and the -registration uri (where you're supposed to POST when the job's done). - -The script modify a template_recipe by replacing every 'instance_name' by the -actual organization name, and same for the combo user extract (rewritting all -the url_redirect fields). - -The script then launch a cook and three commands : -- the import of the combo user with the modified extract -- the import of the combo agent -- a runscript creating a role (same as the one in wcs linked to the form sve -'agents sve'), a provider (the details are in the page linked for the parameter -OZWILLO_PLATEFORM) and an admin User in Authentic who is the ozwillo user -sending the request. - -In the final acknowledgement response, the script sends a 'services' -dictionnary for ozillo to set some links and parameters in its backoffice about -the app deployed). - -OZWILLO_SERVICES is a dict following this pattern: 'service_user': -['service_prefix', 'service_command_manager']. E.G:: - - { - "authentic-multitenant": ["connexion-", "authentic2-multitenant-manage"], - "combo_agent": ["agents-", "combo-manage"], - "combo_usager": ["", "combo-manage"], - "fargo": ["porte-documents-", "fargo-manage"], - "hobo": ["hobo-", "hobo-manage"], - "passerelle": ["passerelle-", "passerelle-manage"], - "wcs": ["demarches-", "wcsctl"] - } - -Destruction -=========== - -For the complete destruction of w.c.s. instances it's necessary that in the -default skeleton (`/var/lib/wcs/skeletons/export.zip`) the `config.pck` file -contains a key `postgresql.createdb-connection-params`. diff --git a/hobo/contrib/ozwillo/__init__.py b/hobo/contrib/ozwillo/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hobo/contrib/ozwillo/admin.py b/hobo/contrib/ozwillo/admin.py deleted file mode 100644 index dc9525d..0000000 --- a/hobo/contrib/ozwillo/admin.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.contrib import admin - -from .models import OzwilloInstance - - -class OzwilloInstanceAdmin(admin.ModelAdmin): - list_display = ['id', 'domain_slug', 'external_ozwillo_id', 'state', 'created', 'modified'] - list_filter = ['state', 'created', 'modified'] - -admin.site.register(OzwilloInstance, OzwilloInstanceAdmin) diff --git a/hobo/contrib/ozwillo/examples/import-site-agents.json b/hobo/contrib/ozwillo/examples/import-site-agents.json deleted file mode 100644 index 866c2e6..0000000 --- a/hobo/contrib/ozwillo/examples/import-site-agents.json +++ /dev/null @@ -1,30 +0,0 @@ -[ - { - "cells": [ - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 1, - "placeholder": "content", - "public": true, - "restricted_to_unlogged": false, - "slug": "services", - "text": "

Bienvenue

\r\n" - }, - "model": "data.textcell" - } - ], - "fields": { - "exclude_from_navigation": false, - "groups": [], - "order": 1, - "parent": null, - "public": false, - "redirect_url": "", - "slug": "index", - "template_name": "standard", - "title": "Accueil" - } - } -] diff --git a/hobo/contrib/ozwillo/examples/import-site-template.json b/hobo/contrib/ozwillo/examples/import-site-template.json deleted file mode 100644 index e3c8aa3..0000000 --- a/hobo/contrib/ozwillo/examples/import-site-template.json +++ /dev/null @@ -1,169 +0,0 @@ -[ - { - "cells": [ - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 0, - "placeholder": "content", - "public": true, - "restricted_to_unlogged": true, - "slug": "", - "text": "

Bienvenue

\r\n\r\n

Bienvenue sur votre compte usager.

\r\n\r\n

Pour suivre vos démarches en cours, créez votre compte personnel ou identifiez-vous depuis la page de connexion.

\r\n" - }, - "model": "data.textcell" - }, - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 1, - "placeholder": "content", - "public": false, - "restricted_to_unlogged": false, - "slug": "", - "text": "

Bienvenue

\r\n\r\n

Bienvenue sur votre compte usager.

\r\n" - }, - "model": "data.textcell" - }, - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 2, - "placeholder": "right", - "public": true, - "restricted_to_unlogged": false, - "slug": "", - "wcs_site": "" - }, - "model": "wcs.trackingcodeinputcell" - }, - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 3, - "placeholder": "right", - "public": true, - "restricted_to_unlogged": true, - "slug": "", - "text": "

Demandes en cours

\r\n\r\n

Connectez-vous pour voir vos demandes en cours.

\r\n" - }, - "model": "data.textcell" - }, - { - "fields": { - "current_forms": true, - "done_forms": false, - "extra_css_class": "", - "groups": [], - "order": 4, - "placeholder": "right", - "public": false, - "restricted_to_unlogged": false, - "slug": "", - "wcs_site": "" - }, - "model": "wcs.wcscurrentformscell" - }, - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 5, - "placeholder": "footer", - "public": true, - "restricted_to_unlogged": false, - "slug": "", - "text": "

Ce service est proposé par le SICTIAM.

\r\n" - }, - "model": "data.textcell" - }, - { - "fields": { - "category_reference": "eservices:nous-contacter", - "extra_css_class": "", - "groups": [], - "limit": null, - "manual_order": { - "data": [] - }, - "order": 6, - "ordering": "popularity", - "placeholder": "content", - "public": true, - "restricted_to_unlogged": false, - "slug": "" - }, - "model": "wcs.wcsformsofcategorycell" - } - ], - "fields": { - "exclude_from_navigation": false, - "groups": [], - "order": 1, - "parent": null, - "public": true, - "redirect_url": "", - "slug": "index", - "template_name": "two-columns", - "title": "Accueil" - } - }, - { - "cells": [ - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 0, - "placeholder": "footer", - "public": true, - "restricted_to_unlogged": false, - "slug": "" - }, - "model": "data.parentcontentcell" - } - ], - "fields": { - "exclude_from_navigation": false, - "groups": [], - "order": 2, - "parent": null, - "public": true, - "redirect_url": "[idp_url]accounts/", - "slug": "mon-compte", - "template_name": "standard", - "title": "Mon compte" - } - }, - { - "cells": [ - { - "fields": { - "extra_css_class": "", - "groups": [], - "order": 0, - "placeholder": "footer", - "public": true, - "restricted_to_unlogged": false, - "slug": "" - }, - "model": "data.parentcontentcell" - } - ], - "fields": { - "exclude_from_navigation": false, - "groups": [], - "order": 3, - "parent": null, - "public": true, - "redirect_url": "[eservices_url]", - "slug": "nous-contacter", - "template_name": "standard", - "title": "Nous contacter" - } - } -] diff --git a/hobo/contrib/ozwillo/examples/template_recipe.json b/hobo/contrib/ozwillo/examples/template_recipe.json deleted file mode 100644 index 5b11fbd..0000000 --- a/hobo/contrib/ozwillo/examples/template_recipe.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "steps": [ - { - "create-hobo": { - "url": "https://${hobo}/", - "primary": true - } - }, - { - "create-authentic": { - "title": "Connexion", - "url": "https://${authentic}/" - } - }, - { - "set-idp": {} - }, - { - "create-combo": { - "template_name": "portal-user", - "title": "Compte citoyen", - "url": "https://${combo}/" - } - }, - { - "create-combo": { - "slug": "portal-agent", - "template_name": "portal-agent", - "title": "Portail agent", - "url": "https://${combo_agent}/" - } - }, - { - "create-wcs": { - "template_name": "export.zip", - "title": "D\u00e9marches", - "url": "https://${wcs}/" - } - }, - { - "create-fargo": { - "title": "Porte documents", - "url": "https://${fargo}/" - } - }, - { - "create-passerelle": { - "title": "Passerelle", - "url": "https://${passerelle}/" - } - }, - { - "set-theme": { - "theme": "publik" - } - } - ], - "variables": { - "authentic": "connexion-instance_name.sictiam.dev.entrouvert.org", - "combo": "instance_name.sictiam.dev.entrouvert.org", - "combo_agent": "agents-instance_name.sictiam.dev.entrouvert.org", - "fargo": "porte-documents-instance_name.sictiam.dev.entrouvert.org", - "hobo": "hobo-instance_name.sictiam.dev.entrouvert.org", - "passerelle": "passerelle-instance_name.sictiam.dev.entrouvert.org", - "wcs": "demarches-instance_name.sictiam.dev.entrouvert.org" - } -} diff --git a/hobo/contrib/ozwillo/management/__init__.py b/hobo/contrib/ozwillo/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hobo/contrib/ozwillo/management/commands/__init__.py b/hobo/contrib/ozwillo/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hobo/contrib/ozwillo/management/commands/ozwillo_worker.py b/hobo/contrib/ozwillo/management/commands/ozwillo_worker.py deleted file mode 100644 index 53328b0..0000000 --- a/hobo/contrib/ozwillo/management/commands/ozwillo_worker.py +++ /dev/null @@ -1,68 +0,0 @@ -# Ozwillo plugin to deploy Publik -# Copyright (C) 2017 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 . - -import logging - -from hobo.contrib.ozwillo.models import OzwilloInstance - -from django.db.transaction import atomic -from django.core.management.base import BaseCommand - - -class Command(BaseCommand): - def add_arguments(self, parser): - parser.add_argument('args', nargs='*') - - @atomic - def handle(self, *args, **options): - qs = OzwilloInstance.objects.select_for_update() - - # deployment - if args: - # allow deploying manually failed instances - to_deploy = qs.fiter( - domain_slug__in=args, - state__in=[OzwilloInstance.STATE_TO_DEPLOY, OzwilloInstance.STATE_DEPLOY_ERROR]) - else: - # deploy everything to be deployed - to_deploy = qs.filter( - state=OzwilloInstance.STATE_TO_DEPLOY) - for instance in to_deploy: - try: - with atomic(): - instance.deploy() - except Exception: - logger.exception(u'ozwillo: deploying %s from CRON failed', instance) - instance.deploy_error() - - # destruction - if args: - # allow deploying manually failed instances - to_destroy = qs.filter( - domain_slug__in=args, - state__in=[OzwilloInstance.STATE_TO_DESTROY, OzwilloInstance.STATE_DESTROY_ERROR]) - else: - # destroy everything to be destroyed - to_destroy = qs.filter(state=OzwilloInstance.STATE_TO_DESTROY) - for instance in to_destroy: - try: - with atomic(): - instance.destroy() - except Exception: - logger.exception(u'ozwillo: destroying %s from CRON failed', instance) - instance.destroy_error() - -logger = logging.getLogger(__name__) diff --git a/hobo/contrib/ozwillo/migrations/0001_initial.py b/hobo/contrib/ozwillo/migrations/0001_initial.py deleted file mode 100644 index 87ff97c..0000000 --- a/hobo/contrib/ozwillo/migrations/0001_initial.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='OzwilloInstance', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('domain_slug', models.CharField(unique=True, max_length=250)), - ('external_ozwillo_id', models.CharField(max_length=450)), - ], - ), - ] diff --git a/hobo/contrib/ozwillo/migrations/0002_auto_20180517_1047.py b/hobo/contrib/ozwillo/migrations/0002_auto_20180517_1047.py deleted file mode 100644 index 75d0d01..0000000 --- a/hobo/contrib/ozwillo/migrations/0002_auto_20180517_1047.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-05-17 10:47 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('ozwillo', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='ozwilloinstance', - name='created', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), - preserve_default=False, - ), - migrations.AddField( - model_name='ozwilloinstance', - name='deploy_data', - field=models.TextField(null=True), - ), - migrations.AddField( - model_name='ozwilloinstance', - name='modified', - field=models.DateTimeField(auto_now=True, default=django.utils.timezone.now), - ), - migrations.AddField( - model_name='ozwilloinstance', - name='state', - field=models.CharField(choices=[(b'new', b'new'), (b'to_deploy', b'to_deploy'), (b'deployed', b'deployed'), (b'deploy_error', b'deploy_error'), (b'to_destroy', b'to_destroy'), (b'destroy_error', b'destroy_error'), (b'destroyed', b'destroyed')], default=b'deployed', max_length=16), - ), - migrations.AlterField( - model_name='ozwilloinstance', - name='external_ozwillo_id', - field=models.CharField(max_length=450, unique=True), - ), - migrations.AlterField( - model_name='ozwilloinstance', - name='modified', - field=models.DateTimeField(auto_now=True), - ), - migrations.AlterField( - model_name='ozwilloinstance', - name='state', - field=models.CharField(choices=[(b'new', b'new'), (b'to_deploy', b'to_deploy'), (b'deployed', b'deployed'), (b'deploy_error', b'deploy_error'), (b'to_destroy', b'to_destroy'), (b'destroy_error', b'destroy_error'), (b'destroyed', b'destroyed')], default=b'new', max_length=16), - ), - ] diff --git a/hobo/contrib/ozwillo/migrations/0003_auto_20180524_0828.py b/hobo/contrib/ozwillo/migrations/0003_auto_20180524_0828.py deleted file mode 100644 index de56993..0000000 --- a/hobo/contrib/ozwillo/migrations/0003_auto_20180524_0828.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-05-24 08:28 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('ozwillo', '0002_auto_20180517_1047'), - ] - - operations = [ - migrations.AlterField( - model_name='ozwilloinstance', - name='domain_slug', - field=models.CharField(max_length=250), - ), - ] diff --git a/hobo/contrib/ozwillo/migrations/__init__.py b/hobo/contrib/ozwillo/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hobo/contrib/ozwillo/models.py b/hobo/contrib/ozwillo/models.py deleted file mode 100644 index db0f713..0000000 --- a/hobo/contrib/ozwillo/models.py +++ /dev/null @@ -1,285 +0,0 @@ -# Ozwillo plugin to deploy Publik -# Copyright (C) 2017 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 . - -import logging -import os -import subprocess -import tempfile -import json - -import requests - -from django.conf import settings -from django.db import models -from django.db.transaction import atomic -from django.utils.text import slugify -from django.core.management import call_command -from django.db import connection - -from tenant_schemas.utils import tenant_context - - -class OzwilloInstance(models.Model): - STATE_NEW = 'new' - STATE_TO_DEPLOY = 'to_deploy' - STATE_DEPLOY_ERROR = 'deploy_error' - STATE_DEPLOYED = 'deployed' - STATE_TO_DESTROY = 'to_destroy' - STATE_DESTROY_ERROR = 'destroy_error' - STATE_DESTROYED = 'destroyed' - - STATES = [ - (STATE_NEW, 'new'), - (STATE_TO_DEPLOY, 'to_deploy'), - (STATE_DEPLOYED, 'deployed'), - (STATE_DEPLOY_ERROR, 'deploy_error'), - (STATE_TO_DESTROY, 'to_destroy'), - (STATE_DESTROY_ERROR, 'destroy_error'), - (STATE_DESTROYED, 'destroyed'), - ] - - state = models.CharField(max_length=16, default=STATE_NEW, choices=STATES) - created = models.DateTimeField(auto_now_add=True) - modified = models.DateTimeField(auto_now=True) - domain_slug = models.CharField(max_length=250) - external_ozwillo_id = models.CharField(max_length=450, unique=True) - deploy_data = models.TextField(null=True) - - def __unicode__(self): - return self.domain_slug - - def __repr__(self): - return '' % ( - self.external_ozwillo_id, self.domain_slug, self.state) - - @property - def data(self): - return json.loads(self.deploy_data) if self.deploy_data else None - - def to_deploy(self): - assert self.state == self.STATE_NEW - try: - with atomic(): - # lock the new instance to prevent collision with the CRON job - OzwilloInstance.objects.select_for_update().filter(id=self.id) - # instance starts with the NEW state, to prevent the CRON from - # deploying instance juste created - # only instance in the state STATE_TO_DEPLOY are deployed. - self.state = self.STATE_TO_DEPLOY - self.save() - self.deploy() - except Exception: - # something failed, still make the instance to be deployed - # an reraise exception - self.state = self.STATE_TO_DEPLOY - self.save() - raise - - def to_destroy(self): - assert self.state == self.STATE_DEPLOYED - self.state = self.STATE_TO_DESTROY - self.save() - - def deploy_error(self): - assert self.state in [self.STATE_DEPLOY_ERROR, self.STATE_TO_DEPLOY] - if self.state == self.STATE_TO_DEPLOY: - self.state = self.STATE_DEPLOY_ERROR - self.save() - - def deploy(self): - logger.info(u'ozwillo: deploy start for %s', self) - data = self.data - if not data: - logger.warning(u'ozwillo: unable to deploy, no data') - return - - # Request parsing - client_id = data['client_id'] - client_secret = data['client_secret'] - instance_id = data['instance_id'] - instance_name = data['organization_name'] - instance_name = slugify(instance_name) - registration_uri = data['instance_registration_uri'] - user = data['user'] - - # Cook new platform - template_recipe = json.load(open('/etc/hobo/ozwillo/template_recipe.json', 'rb')) - var = template_recipe['variables'] - for key, value in var.items(): - var[key] = value.replace('instance_name', instance_name) - - template_recipe['variables'] = var - domain = var['combo'] - domain_agent = var['combo_agent'] - domain_passerelle = var['passerelle'] - logger.info(u'ozwillo: cooking %s', template_recipe) - - with tempfile.NamedTemporaryFile() as recipe_file: - json.dump(template_recipe, recipe_file) - recipe_file.flush() - - # cook play with the tenant context, we must protect from that - with tenant_context(connection.tenant): - call_command('cook', recipe_file.name, timeout=1000, verbosity=0) - - # Load user portal template - logger.info(u'ozwillo: loading combo template') - run_command([ - 'sudo', '-u', 'combo', - 'combo-manage', 'tenant_command', 'import_site', - '/etc/hobo/ozwillo/import-site-template.json', - '-d', domain - ]) - - # Load agent portal template - logger.info(u'ozwillo: loading combo agent template') - run_command([ - 'sudo', '-u', 'combo', - 'combo-manage', 'tenant_command', 'import_site', - '/etc/hobo/ozwillo/import-site-agents.json', - '-d', domain_agent - ]) - - # Configure OIDC Ozwillo authentication - logger.info(u'ozwillo: configuring OIDC ozwillo authentication') - domain_name = 'connexion-%s.%s' % (instance_name, settings.OZWILLO_ENV_DOMAIN) - if run_command([ - 'sudo', '-u', 'authentic-multitenant', - 'authentic2-multitenant-manage', 'tenant_command', 'oidc-register-issuer', - '-d', domain_name, - '--scope', 'profile', - '--scope', 'email', - '--issuer', settings.OZWILLO_PLATEFORM, - '--client-id', client_id, - '--client-secret', client_secret, - '--claim-mapping', 'given_name first_name always_verified', - '--claim-mapping', 'family_name last_name always_verified', - '--ou-slug', 'default', - '--claim-mapping', 'email email required', - 'Ozwillo' - ]): - # creation of the admin user depends upon the creation of provider - logger.info(u'ozwillo: creating admin user') - create_user_script = os.path.dirname(__file__) + '/scripts/create_user_ozwillo.py' - run_command([ - 'sudo', '-u', 'authentic-multitenant', - 'authentic2-multitenant-manage', 'tenant_command', 'runscript', '-d', domain_name, - create_user_script, user['email_address'], user['id'], user['name'] - ]) - - # Load passerelle template - logger.info(u'ozwillo: loading passerelle template') - run_command([ - 'sudo', '-u', 'passerelle', - 'passerelle-manage', 'tenant_command', 'import_site', - '/etc/hobo/ozwillo/import-site-passerelle.json', - '--import-user', '-d', domain_passerelle - ]) - - # Sending done event to Ozwillo - services = { - 'services': [{ - 'local_id': 'publik', - 'name': 'Publik - %s' % (instance_name), - 'service_uri': 'https://connexion-%s.%s/accounts/oidc/login?iss=%s' - % (instance_name, settings.OZWILLO_ENV_DOMAIN, - settings.OZWILLO_PLATEFORM), - 'description': 'Gestion de la relation usagers', - 'tos_uri': 'https://publik.entrouvert.com/', - 'policy_uri': 'https://publik.entrouvert.com/', - 'icon': 'https://publik.entrouvert.com/static/img/logo-publik-64x64.png', - 'payment_option': 'FREE', - 'target_audience': ['PUBLIC_BODIES', - 'CITIZENS', - 'COMPANIES'], - 'contacts': ['https://publik.entrouvert.com/'], - 'redirect_uris': ['https://connexion-%s.%s/accounts/oidc/callback/' - % (instance_name, settings.OZWILLO_ENV_DOMAIN)], - }], - 'instance_id': instance_id, - 'destruction_uri': settings.OZWILLO_DESTRUCTION_URI, - 'destruction_secret': settings.OZWILLO_DESTRUCTION_SECRET, - 'needed_scopes': [] - } - logger.info(u'ozwillo: sending registration request, %r', services) - headers = {'Content-type': 'application/json', 'Accept': 'application/json'} - response = requests.post( - registration_uri, - data=json.dumps(services), - auth=(client_id, client_secret), - headers=headers) - logger.info(u'ozwillo: registration response, status=%s content=%r', - response.status_code, response.content) - self.state = self.STATE_DEPLOYED - self.save() - logger.info(u'ozwillo: deploy finished') - - def destroy_error(self): - assert self.state in [self.STATE_DESTROY_ERROR, self.STATE_TO_DESTROY] - if self.state == self.STATE_TO_DESTROY: - self.state = self.STATE_DESTROY_ERROR - self.save() - - - def destroy(self): - logger.info(u'ozwillo: destroy start for %s', self) - instance_slug = self.domain_slug - services = settings.OZWILLO_SERVICES.copy() - # w.c.s. is handled differently - wcs = services.pop('wcs') - - for s, infos in services.items(): - # to get the two combo instances which have same service - service_user = s.split('_')[0] - - tenant = '%s%s.%s' % (infos[0], instance_slug, settings.OZWILLO_ENV_DOMAIN) - - run_command([ - 'sudo', '-u', service_user, infos[1], - 'delete_tenant', '--force-drop', tenant - ]) - - tenant = '%s%s.%s' % (wcs[0], instance_slug, settings.OZWILLO_ENV_DOMAIN) - run_command([ - 'sudo', '-u', 'wcs', - wcs[1], '-f', '/etc/wcs/wcs-au-quotidien.cfg', - 'delete_tenant', '--force-drop', tenant - ]) - - logger.info(u'ozwillo: destroy finished') - self.state = self.STATE_DESTROYED - self.save() - - -def run_command(args): - try: - process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except OSError as e: - logger.error('ozwillo: launching subprocess %s raised error %s', args, e) - return False - logger.info('ozwillo: launching subprocess with pid %s : %s', process.pid, args) - stdoutdata, stderrdata = process.communicate() - if process.returncode != 0: - logger.error('ozwillo: subprocess %s failed returncode=%s stdout=%r stderr=%r', - process.pid, process.returncode, stdoutdata, stderrdata) - return False - logger.info('ozwillo: subprocess terminated') - return True - - -logger = logging.getLogger(__name__) - - diff --git a/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py b/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py deleted file mode 100644 index d8d1658..0000000 --- a/hobo/contrib/ozwillo/scripts/create_user_ozwillo.py +++ /dev/null @@ -1,55 +0,0 @@ -import sys -import logging - -from django.contrib.auth import get_user_model - -from authentic2_auth_oidc.models import OIDCAccount -from authentic2_auth_oidc.models import OIDCProvider -from authentic2.a2_rbac.models import Role, OrganizationalUnit - - -def create_user(user_id, email_addressi, user_name, provider): - while True: - new_user = User.objects.create() - oidc_account, created = OIDCAccount.objects.select_related().get_or_create(provider=provider, sub=user_id, defaults={'user': new_user}) - if created: - if OIDCAccount.objects.filter(provider=provider, sub=user_id).count() > 1: - oidc_account.delete() - new_user.delete() - continue - break - else: - new_user.delete() - new_user = oidc_account.user - break - - new_user.email = email_address - new_user.ou = provider.ou - new_user.is_superuser = True - new_user.is_staff = True - new_user.first_name = 'admin' - new_user.last_name = user_name - new_user.save() - - return new_user - - -# create agent_sve role -ou = OrganizationalUnit.objects.get(default=True) -role, created = Role.objects.get_or_create(uuid='3f3367d817bb4a9aa98e7ed6c83b5b09', - defaults={'name': u'Agents SVE', - 'ou': ou}) - -# set the provider for the user creation -provider = OIDCProvider.objects.get(name='Ozwillo') - -# get user info from args sent to the script -args = sys.argv -email_address = args[1] -user_id = args[2] -user_name = args[3] - -# create admin user in Publik from Ozwillo -User = get_user_model() -user = create_user(user_id, email_address, user_name, provider) -logging.info('owzillo provisionning: user created with uuid: %s', user.uuid) diff --git a/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py b/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py deleted file mode 100644 index 1ab4bfd..0000000 --- a/hobo/contrib/ozwillo/scripts/synchronize_ozwillo_users.py +++ /dev/null @@ -1,148 +0,0 @@ -# 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 . - -import json -import datetime -import logging -from urllib import urlencode -import pprint -import os - -import requests -from robobrowser.browser import RoboBrowser - -from django.utils.six.moves.urllib import parse as urlparse - -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() - - 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) diff --git a/hobo/contrib/ozwillo/urls.py b/hobo/contrib/ozwillo/urls.py deleted file mode 100644 index 04887f3..0000000 --- a/hobo/contrib/ozwillo/urls.py +++ /dev/null @@ -1,25 +0,0 @@ -# Ozwillo plugin to deploy -# Copyright (C) 2017 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 . - -from django.conf.urls import url, include - -from . import views - - -urlpatterns = [ - url(r'create-publik-instance', views.create_publik_instance, name='ozwillo-create-publik-instance'), - url(r'delete-publik-instance', views.delete_publik_instance, name='ozwillo-delete-publik-instance'), -] diff --git a/hobo/contrib/ozwillo/views.py b/hobo/contrib/ozwillo/views.py deleted file mode 100644 index 1862727..0000000 --- a/hobo/contrib/ozwillo/views.py +++ /dev/null @@ -1,144 +0,0 @@ -# Ozwillo plugin to deploy Publik -# Copyright (C) 2017 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 . - -import logging -import json -import hmac -import threading -from hashlib import sha1 - -from django.views.decorators.csrf import csrf_exempt -from django.conf import settings -from django.http import (HttpResponseForbidden, HttpResponseBadRequest, - HttpResponseNotFound, HttpResponse) -from django.utils.text import slugify -from django.db import DatabaseError -from django.db.transaction import atomic - -from .models import OzwilloInstance - -logger = logging.getLogger(__name__) - - -def valid_signature_required(setting): - '''Validate Ozwillo signatures''' - signature_header_name = 'HTTP_X_HUB_SIGNATURE' - - def decorator(func): - def wrapper(request, *args, **kwargs): - api_secret = getattr(settings, setting) - if signature_header_name in request.META: - if request.META[signature_header_name].startswith('sha1='): - algo, received_hmac = request.META[signature_header_name].rsplit('=') - computed_hmac = hmac.new(api_secret, request.content, sha1).hexdigest() - # the received hmac is uppercase according to - # http://doc.ozwillo.com/#ref-3-2-1 - if received_hmac.lower() != computed_hmac: - logger.error(u'ozwillo: invalid HMAC') - return HttpResponseForbidden('invalid HMAC') - else: - logger.error(u'ozwillo: invalid HMAC algo') - return HttpResponseForbidden('invalid HMAC algo') - else: - logger.error(u'ozwillo: no HMAC in the header') - return HttpResponseForbidden('no HMAC in the header') - return func(request, *args, **kwargs) - return wrapper - return decorator - - -def is_ozwillo_enabled(func): - def wrapper(request): - if not getattr(settings, 'OZWILLO_ENABLED', False): - return HttpResponseNotFound('owillo providing is not active here.') - return func(request) - return wrapper - - -@csrf_exempt -@is_ozwillo_enabled -@valid_signature_required(setting='OZWILLO_SECRET') -def create_publik_instance(request): - try: - data = json.loads(request.text) - except ValueError: - logger.warning(u'ozwillo: received non JSON request') - return HttpResponseBadRequest('invalid JSON content') - - logger.info(u'ozwillo: create publik instance request, %r', data) - - if 'organization_name' not in data.keys(): - logger.warning(u'ozwillo: missing organization_name') - return HttpResponseBadRequest('missing parameter "organization_name"') - - org_name = slugify(data['organization_name']) - # forbid creation of an instance if a not destroyed previous one exists - if OzwilloInstance.objects.exclude(state=OzwilloInstance.STATE_DESTROYED).filter(domain_slug=org_name): - logger.warning(u'ozwillo: instance %s already exists', org_name) - return HttpResponseBadRequest('instance %s already exists' % org_name) - - try: - instance = OzwilloInstance.objects.create( - external_ozwillo_id=data['instance_id'], - state=OzwilloInstance.STATE_NEW, - domain_slug=org_name, - # deploy_data is a TextField containing JSON - deploy_data=json.dumps(data, indent=4)) - except DatabaseError as e: - logger.warning(u'ozwillo: could not create instance_id %r org_name %r: %r', - data['instance_id'], org_name, e) - return HttpResponseBadRequest(u'cannot create the instance: %r', e) - - # immediate deploy in a thread - def thread_function(data): - try: - instance.to_deploy() - except Exception: - logger.exception(u'ozwillo: error occured duging initial deploy request %s', org_name) - thread = threading.Thread(target=thread_function, args=(data,)) - thread.start() - - return HttpResponse() - - -@csrf_exempt -@is_ozwillo_enabled -@valid_signature_required(setting='OZWILLO_DESTRUCTION_SECRET') -@atomic -def delete_publik_instance(request): - try: - data = json.loads(request.text) - except ValueError: - logger.warning(u'ozwillo: received non JSON request') - return HttpResponseBadRequest('invalid JSON content') - - logger.info(u'ozwillo: delete publik instance request (%r)', data) - - try: - instance_id = data['instance_id'] - except KeyError: - logger.warning(u'ozwillo: no instance id in destroy request (%r)', data) - return HttpResponseBadRequest('no instance id') - try: - instance = OzwilloInstance.objects.select_for_update().get( - external_ozwillo_id=instance_id, - # only deployed instances can be destroyed - state=OzwilloInstance.STATE_DEPLOYED) - except OzwilloInstance.DoesNotExist: - return HttpResponseBadRequest('no instance with id %s' % data['instance_id']) - instance.to_destroy() - logger.info(u'ozwillo: destroy registered for %s (%s)', instance.domain_slug, instance.external_ozwillo_id) - return HttpResponse(status=200) diff --git a/hobo/urls.py b/hobo/urls.py index 6bb1df6..d61d60c 100644 --- a/hobo/urls.py +++ b/hobo/urls.py @@ -57,8 +57,3 @@ urlpatterns += [ url(r'^login/local/$', login_local), # to be used as backup, in case of idp down url(r'^accounts/mellon/', include('mellon.urls')), ] - -if 'hobo.contrib.ozwillo' in settings.INSTALLED_APPS: - urlpatterns += [ - url(r'ozwillo/', include('hobo.contrib.ozwillo.urls')), - ] diff --git a/tests/settings.py b/tests/settings.py index c77a383..ef37567 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -2,9 +2,8 @@ import os LANGUAGE_CODE = 'en-us' BROKER_URL = 'memory://' -OZWILLO_SECRET = 'secret' -INSTALLED_APPS += ('hobo.contrib.ozwillo', 'hobo.agent.common') +INSTALLED_APPS += ('hobo.agent.common', ) ALLOWED_HOSTS.append('localhost')