From feeb379d5d699e311f10e811603f81408611efa2 Mon Sep 17 00:00:00 2001
From: Emmanuel Cazenave <ecazenave@entrouvert.com>
Date: Thu, 13 Jan 2022 17:56:23 +0100
Subject: [PATCH] environment: store local hobo info in Hobo model (#60572)

---
 .../hobo/management/commands/hobo_deploy.py   |  2 +-
 .../environment/migrations/0022_local_hobo.py | 18 ++++++
 .../migrations/0023_populate_local_hobo.py    | 43 ++++++++++++++
 hobo/environment/models.py                    | 16 ++++-
 hobo/environment/utils.py                     | 59 ++++++++++++-------
 tests_multipublik/test_multipublik.py         |  4 +-
 6 files changed, 117 insertions(+), 25 deletions(-)
 create mode 100644 hobo/environment/migrations/0022_local_hobo.py
 create mode 100644 hobo/environment/migrations/0023_populate_local_hobo.py

diff --git a/hobo/agent/hobo/management/commands/hobo_deploy.py b/hobo/agent/hobo/management/commands/hobo_deploy.py
index 4d58ee0..b188da7 100644
--- a/hobo/agent/hobo/management/commands/hobo_deploy.py
+++ b/hobo/agent/hobo/management/commands/hobo_deploy.py
@@ -24,7 +24,7 @@ class Command(hobo_deploy.Command):
             if me.get('secondary'):
                 # check we are currently processing the primary hobo, we don't
                 # want to create secondary services on every branches.
-                if Hobo.objects.filter(secondary=False).count() == 0:
+                if Hobo.objects.filter(secondary=False, local=False).count() == 0:
                     return
                 is_primary_hobo = True
                 # on the primary hobo, notify other primary services, this will
diff --git a/hobo/environment/migrations/0022_local_hobo.py b/hobo/environment/migrations/0022_local_hobo.py
new file mode 100644
index 0000000..a0d6f40
--- /dev/null
+++ b/hobo/environment/migrations/0022_local_hobo.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.24 on 2022-02-15 10:33
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('environment', '0021_base_url_validators'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='hobo',
+            name='local',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/hobo/environment/migrations/0023_populate_local_hobo.py b/hobo/environment/migrations/0023_populate_local_hobo.py
new file mode 100644
index 0000000..654d2b5
--- /dev/null
+++ b/hobo/environment/migrations/0023_populate_local_hobo.py
@@ -0,0 +1,43 @@
+# Generated by Django 2.2.24 on 2022-02-15 10:36
+
+from django.db import connection, migrations
+from django.urls import reverse
+
+from hobo.environment.utils import get_local_key
+
+
+def populate_local_hobo(apps, schema_editor):
+    Hobo = apps.get_model('environment', 'Hobo')
+    try:
+        Hobo.objects.get(local=True)
+        return
+    except Hobo.DoesNotExist:
+        pass
+
+    if hasattr(connection, 'get_tenant'):
+        build_absolute_uri = getattr(connection.get_tenant(), 'build_absolute_uri', None)
+        if build_absolute_uri:
+            Hobo.objects.create(
+                secret_key=get_local_key(build_absolute_uri('/')),
+                title='Hobo',
+                slug='hobo',
+                base_url=build_absolute_uri(reverse('home')),
+                secondary=False,
+                local=True,
+            )
+
+
+def clean_local_hobo(apps, schema_editor):
+    Hobo = apps.get_model('environment', 'Hobo')
+    Hobo.objects.filter(local=True).delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('environment', '0022_local_hobo'),
+    ]
+
+    operations = [
+        migrations.RunPython(populate_local_hobo, clean_local_hobo),
+    ]
diff --git a/hobo/environment/models.py b/hobo/environment/models.py
index 75436a3..43de8f1 100644
--- a/hobo/environment/models.py
+++ b/hobo/environment/models.py
@@ -459,6 +459,8 @@ class Hobo(ServiceBase):
         service_label = _('Deployment Server')
         service_default_slug = 'hobo'
 
+    local = models.BooleanField(default=False)
+
     def get_admin_zones(self):
         return [Zone(self.title, 'hobo', self.get_base_url_path())]
 
@@ -466,7 +468,19 @@ class Hobo(ServiceBase):
         return self.get_base_url_path() + 'accounts/mellon/metadata/'
 
     def get_backoffice_menu_url(self):
-        return None
+        return self.get_base_url_path() + 'menu.json'
+
+    def as_dict(self):
+        res = super().as_dict()
+        del res['local']
+        if self.local:
+            del res['id']
+            del res['secret_key']
+            del res['secondary']
+            del res['service-label']
+            del res['template_name']
+            del res['variables']
+        return res
 
 
 class BiJoe(ServiceBase):
diff --git a/hobo/environment/utils.py b/hobo/environment/utils.py
index ebc3e2d..48370ec 100644
--- a/hobo/environment/utils.py
+++ b/hobo/environment/utils.py
@@ -28,11 +28,14 @@ from hobo.profile.utils import get_profile_dict
 
 
 def get_installed_services():
-    from .models import AVAILABLE_SERVICES
+    from .models import AVAILABLE_SERVICES, Hobo
 
     installed_services = []
     for available_service in AVAILABLE_SERVICES:
-        installed_services.extend(available_service.objects.all())
+        if available_service is Hobo:
+            installed_services.extend(available_service.objects.filter(local=False))
+        else:
+            installed_services.extend(available_service.objects.all())
     return installed_services
 
 
@@ -46,27 +49,39 @@ def get_local_key(url):
     return KnownServices.shared_secret(secret1, secret2)[:40]
 
 
+def get_or_create_local_hobo():
+    from .models import Hobo
+
+    try:
+        hobo = Hobo.objects.get(local=True)
+    except Hobo.DoesNotExist:
+        build_absolute_uri = None
+        if hasattr(connection, 'get_tenant') and hasattr(connection.get_tenant(), 'build_absolute_uri'):
+            build_absolute_uri = connection.get_tenant().build_absolute_uri
+        else:
+            request = StoreRequestMiddleware.get_request()
+            if request:
+                build_absolute_uri = request.build_absolute_uri
+        if not build_absolute_uri:
+            return None
+
+        hobo = Hobo.objects.create(
+            secret_key=get_local_key(build_absolute_uri('/')),
+            title='Hobo',
+            slug='hobo',
+            base_url=build_absolute_uri(reverse('home')),
+            secondary=False,
+            local=True,
+        )
+
+    return hobo
+
+
 def get_local_hobo_dict():
-    build_absolute_uri = None
-    if hasattr(connection, 'get_tenant') and hasattr(connection.get_tenant(), 'build_absolute_uri'):
-        build_absolute_uri = connection.get_tenant().build_absolute_uri
-    else:
-        request = StoreRequestMiddleware.get_request()
-        if request:
-            build_absolute_uri = request.build_absolute_uri
-    if not build_absolute_uri:
-        return None
-    # if there's a known base url hobo can advertise itself.
-    return {
-        'secret_key': get_local_key(build_absolute_uri('/')),
-        'service-id': 'hobo',
-        'title': 'Hobo',
-        'slug': 'hobo',
-        'base_url': build_absolute_uri(reverse('home')),
-        'saml-sp-metadata-url': build_absolute_uri(reverse('mellon_metadata')),
-        'backoffice-menu-url': build_absolute_uri(reverse('menu_json')),
-        'provisionning-url': build_absolute_uri('/__provision__/'),
-    }
+    hobo = get_or_create_local_hobo()
+    if hobo:
+        return hobo.as_dict()
+    return None
 
 
 def get_installed_services_dict():
diff --git a/tests_multipublik/test_multipublik.py b/tests_multipublik/test_multipublik.py
index e0ee696..807579b 100644
--- a/tests_multipublik/test_multipublik.py
+++ b/tests_multipublik/test_multipublik.py
@@ -88,7 +88,9 @@ def test_multipublik(tenants, mocker):
     # (no service creation recursion)
     HoboDeployCommand().handle(hobo2.base_url, get_hobo_json_filename(hobo1))
     with tenant_context(hobo2):
-        assert Hobo.objects.filter().count() == 2
+        # +1 on hobo since we last checked because the local hobo
+        # is not stored in DB until the first hobo_json is generated
+        assert Hobo.objects.filter().count() == 3
         assert Hobo.objects.filter(secondary=True).count() == 2
         assert Combo.objects.filter().count() == 2
         assert Combo.objects.filter(secondary=True).count() == 1
-- 
2.30.2

