hobo/hobo/provisionning/middleware.py

116 lines
5.0 KiB
Python

# 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 <http://www.gnu.org/licenses/>.
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']
)