changeset 1362:45ebeea1bacd

server: improved service profile check + auto creation: When service profile can't be connected, bridge exception is checked and if the profile doesn't exists yet, Libervia tried to create it using in-band registration.
author Goffi <goffi@goffi.org>
date Sat, 14 Nov 2020 22:27:49 +0100
parents 626b7bbb7f90
children c3dac1e11341
files libervia/server/server.py
diffstat 1 files changed, 127 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/server/server.py	Sat Nov 14 22:24:58 2020 +0100
+++ b/libervia/server/server.py	Sat Nov 14 22:27:49 2020 +0100
@@ -54,6 +54,7 @@
 from sat.tools.common.utils import recursive_update, OrderedSet
 from sat.tools.common import data_format
 from sat.tools.common import tls
+from sat_frontends.bridge.bridge_frontend import BridgeException
 import libervia
 from libervia.server import websockets
 from libervia.server.pages import LiberviaPage
@@ -932,16 +933,132 @@
         if default_dict:
             conf[''] = default_dict
 
+    async def checkAndConnectServiceProfile(self):
+        passphrase = self.options["passphrase"]
+        if not passphrase:
+            raise SysExit(
+                C.EXIT_BAD_ARG,
+                _("No passphrase set for service profile, please check installation "
+                  "documentation.")
+            )
+        try:
+            s_prof_connected = await self.bridgeCall("isConnected", C.SERVICE_PROFILE)
+        except BridgeException as e:
+            if e.classname == "ProfileUnknownError":
+                log.info("Service profile doesn't exist, creating it.")
+                try:
+                    xmpp_domain = await self.bridgeCall("getConfig", "", "xmpp_domain")
+                    xmpp_domain = xmpp_domain.strip()
+                    if not xmpp_domain:
+                        raise SysExit(
+                            C.EXIT_BAD_ARG,
+                            _('"xmpp_domain" must be set to create new accounts, please '
+                              'check documentation')
+                        )
+                    service_profile_jid_s = f"{C.SERVICE_PROFILE}@{xmpp_domain}"
+                    await self.bridgeCall(
+                        "inBandAccountNew",
+                        service_profile_jid_s,
+                        passphrase,
+                        "",
+                        xmpp_domain,
+                        0,
+                    )
+                except BridgeException as e:
+                    if e.condition == "conflict":
+                        log.info(
+                            _("Service's profile JID {profile_jid} already exists")
+                            .format(profile_jid=service_profile_jid_s)
+                        )
+                    elif e.classname == "UnknownMethod":
+                        raise SysExit(
+                            C.EXIT_BRIDGE_ERROR,
+                            _("Can't create service profile XMPP account, In-Band "
+                              "Registration plugin is not activated, you'll have to "
+                              "create the {profile!r} profile with {profile_jid!r} JID "
+                              "manually.").format(
+                                  profile=C.SERVICE_PROFILE,
+                                  profile_jid=service_profile_jid_s)
+                        )
+                    elif e.condition == "service-unavailable":
+                        raise SysExit(
+                            C.EXIT_BRIDGE_ERROR,
+                            _("Can't create service profile XMPP account, In-Band "
+                              "Registration is not activated on your server, you'll have "
+                              "to create the {profile!r} profile with {profile_jid!r} JID "
+                              "manually.\nNote that you'll need to activate In-Band "
+                              "Registation on your server if you want users to be able "
+                              "to create new account from {app_name}, please check "
+                              "documentation.").format(
+                                  profile=C.SERVICE_PROFILE,
+                                  profile_jid=service_profile_jid_s,
+                                  app_name=C.APP_NAME)
+                        )
+                    elif e.condition == "not-acceptable":
+                        raise SysExit(
+                            C.EXIT_BRIDGE_ERROR,
+                            _("Can't create service profile XMPP account, your XMPP "
+                              "server doesn't allow us to create new accounts with "
+                              "In-Band Registration please check XMPP server "
+                              "configuration: {reason}"
+                              ).format(
+                                  profile=C.SERVICE_PROFILE,
+                                  profile_jid=service_profile_jid_s,
+                                  reason=e.message)
+                        )
+
+                    else:
+                        raise SysExit(
+                            C.EXIT_BRIDGE_ERROR,
+                            _("Can't create service profile XMPP account, you'll have "
+                              "do to it manually: {reason}").format(reason=e.message)
+                        )
+                try:
+                    await self.bridgeCall("profileCreate", C.SERVICE_PROFILE, passphrase)
+                    await self.bridgeCall(
+                        "profileStartSession", passphrase, C.SERVICE_PROFILE)
+                    await self.bridgeCall(
+                        "setParam", "JabberID", service_profile_jid_s, "Connection", -1,
+                        C.SERVICE_PROFILE)
+                    await self.bridgeCall(
+                        "setParam", "Password", passphrase, "Connection", -1,
+                        C.SERVICE_PROFILE)
+                except BridgeException as e:
+                    raise SysExit(
+                        C.EXIT_BRIDGE_ERROR,
+                        _("Can't create service profile XMPP account, you'll have "
+                          "do to it manually: {reason}").format(reason=e.message)
+                    )
+                log.info(_("Service profile has been successfully created"))
+                s_prof_connected = False
+            else:
+                raise SysExit(C.EXIT_BRIDGE_ERROR, e.message)
+
+        if not s_prof_connected:
+            try:
+                await self.bridgeCall(
+                    "connect",
+                    C.SERVICE_PROFILE,
+                    passphrase,
+                    {},
+                )
+            except BridgeException as e:
+                raise SysExit(
+                    C.EXIT_BRIDGE_ERROR,
+                    _("Connection of service profile failed: {reason}").format(reason=e)
+                )
+
     async def backendReady(self):
         log.info(f"Libervia v{self.full_version}")
+
+        # settings
         if self.options['dev_mode']:
             log.info(_("Developer mode activated"))
-        self.media_dir = self.bridge.getConfig("", "media_dir")
-        self.local_dir = self.bridge.getConfig("", "local_dir")
+        self.media_dir = await self.bridgeCall("getConfig", "", "media_dir")
+        self.local_dir = await self.bridgeCall("getConfig", "", "local_dir")
         self.cache_root_dir = os.path.join(self.local_dir, C.CACHE_DIR)
         self.renderer = template.Renderer(self, self._front_url_filter)
         sites_names = list(self.renderer.sites_paths.keys())
-        self.restricted_bridge = RestrictedBridge(self)
 
         self._moveFirstLevelToDict(self.options, "url_redirections_dict", sites_names)
         self._moveFirstLevelToDict(self.options, "menu_json", sites_names)
@@ -952,6 +1069,12 @@
         for site, value in self.options["menu_extra_json"].items():
             menu[site].extend(value)
 
+        # service profile
+        await self.checkAndConnectServiceProfile()
+
+        # restricted bridge, the one used by browser code
+        self.restricted_bridge = RestrictedBridge(self)
+
         # we create virtual hosts and import Libervia pages into them
         self.vhost_root = vhost.NameVirtualHost()
         default_site_path = Path(libervia.__file__).parent.resolve()
@@ -1436,34 +1559,7 @@
     def startService(self):
         """Connect the profile for Libervia and start the HTTP(S) server(s)"""
         self._init()
-
-        def eb(e):
-            log.error(_("Connection failed: %s") % e)
-            self.stop()
-
-        def initOk(__):
-            try:
-                connected = self.bridge.isConnected(C.SERVICE_PROFILE)
-            except Exception as e:
-                # we don't want the traceback
-                msg = [l for l in str(e).split("\n") if l][-1]
-                log.error(
-                    "Can't check service profile ({profile}), are you sure it exists ?"
-                    "\n{error}".format(profile=C.SERVICE_PROFILE, error=msg))
-                self.stop()
-                return
-            if not connected:
-                self.bridge.connect(
-                    C.SERVICE_PROFILE,
-                    self.options["passphrase"],
-                    {},
-                    callback=self._startService,
-                    errback=eb,
-                )
-            else:
-                self._startService()
-
-        self.initialised.addCallback(initOk)
+        self.initialised.addCallback(self._startService)
         self.initialised.addErrback(self.initEb)
 
     ## URLs ##