# hobo - portal to configure and deploy applications # Copyright (C) 2015-2020 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 logging import sys import urllib.parse from django.conf import settings from django.db import connection from django.http import HttpResponseBadRequest, HttpResponseForbidden, JsonResponse from django.utils.deprecation import MiddlewareMixin from django.utils.encoding import force_bytes, force_str from hobo.provisionning.utils import NotificationProcessing from hobo.rest_authentication import PublikAuthentication, PublikAuthenticationFailed logger = logging.getLogger(__name__) class ProvisionningMiddleware(MiddlewareMixin, NotificationProcessing): def process_request(self, request): if not (request.method == 'PUT' and request.path == '/__provision__/'): return None if 'hobo.environment' in settings.INSTALLED_APPS: self.hobo_specific_setup() try: user_auth_tuple = PublikAuthentication().authenticate(request) except PublikAuthenticationFailed: return HttpResponseForbidden() if user_auth_tuple is None: return HttpResponseForbidden() try: notification = json.loads(force_str(request.body)) except ValueError: return HttpResponseBadRequest() if not isinstance(notification, dict) or 'objects' not in notification: return HttpResponseBadRequest() object_type = notification['objects'].get('@type') issuer = notification.get('issuer') action = notification.get('@type') if not (object_type and action): return HttpResponseBadRequest() full = notification['full'] if 'full' in notification else False data = notification['objects']['data'] msg = 'received request for %sing %%d %%s objects (HTTP)' % action logger.info(msg, len(notification['objects']['data']), object_type) if 'uwsgi' in sys.modules and 'sync' not in request.GET: from hobo.provisionning.spooler import provision tenant = getattr(connection, 'tenant', None) domain = getattr(tenant, 'domain_url', '') object_type = object_type or '' domain = domain or '' issuer = issuer or '' action = action or '' full = 'true' if full else 'false' body = json.dumps(data) provision.spool( object_type=force_bytes(object_type), domain=force_bytes(domain), issuer=force_bytes(issuer), action=force_bytes(action), body=force_bytes(body), full=force_bytes(full), ) else: self.provision(object_type=object_type, issuer=issuer, action=action, data=data, full=full) return JsonResponse({'err': 0}) def hobo_specific_setup(self): # much ado about hobo, this is because it is not deployed like other # services and will not have a hobo.json in its tenant directory, and # will thus be missing settings loaders, etc. from hobo.environment.utils import get_local_hobo_dict known_services = getattr(settings, 'KNOWN_SERVICES', None) local_hobo_dict = get_local_hobo_dict() if not known_services: # hobo in a single deployment instance settings.KNOWN_SERVICES = known_services = {} known_services['hobo'] = {'hobo': local_hobo_dict} known_services['authentic'] = {'idp': {}} if known_services['hobo']['hobo']['provisionning-url'] == local_hobo_dict['provisionning-url']: # hobo in a single deployment instance, or primary hobo in a # multi-instances environment from hobo.environment.models import Authentic from hobo.multitenant.settings_loaders import KnownServices authentic = Authentic.objects.all().first() orig = urllib.parse.urlparse(authentic.base_url).netloc.split(':')[0] # create stub settings.KNOWN_SERVICES with just enough to get # authentication passing. idp_service = list(settings.KNOWN_SERVICES['authentic'].values())[0] idp_service['verif_orig'] = orig idp_service['secret_key'] = KnownServices.shared_secret( authentic.secret_key, local_hobo_dict['secret_key'] )