def _start_node(self, options, reactor): self.node = Node(options.cbdir, reactor=reactor) self.node.log = LoggerBridge() self.pubkey = self.node.maybe_generate_key(options.cbdir) checkconfig.check_config(self.config) self.node._config = self.config return self.node.start()
def start_crossbar(): from crossbar.controller.node import Node node = Node(reactor, options) d = node.start() def on_error(err): log.error("Could not start node: {error}", error=err.value) if reactor.running: reactor.stop() d.addErrback(on_error)
def start(self, reactor, options=None): # imports reactor from crossbar.controller.node import Node, default_native_workers options = options or self.options if self.address.ssl: self.cert_manager.generate_if_needed() self.node = Node(options.cbdir, reactor=reactor) self.pubkey = self.node.maybe_generate_key(options.cbdir) workers = default_native_workers() checkconfig.check_config(self.config, workers) self.node._config = self.config return self.node.start()
def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath( os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format( "Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 30 + " Crossbar.io " + "=" * 30 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format( platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run_command_start(options): """ Subcommand "crossbar start". """ ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath(os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system = "{:<10} {:>6}".format("Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 30 + " Crossbar.io " + "=" * 30 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format(platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run_command_start(options, reactor=None): """ Subcommand "crossbar start". """ assert reactor # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print( "Crossbar.io is already running from node directory {} (PID {}).". format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'wb') as fd: argv = options.argv options_dump = vars(options) pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': { x: y for x, y in options_dump.items() if x not in ["func", "argv"] } } fd.write("{}\n".format( json.dumps(pid_data, sort_keys=False, indent=4, separators=(', ', ': '), ensure_ascii=False)).encode('utf8')) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) log = make_logger() # possibly generate new node key # from crossbar.controller.node import maybe_generate_key pubkey = maybe_generate_key(log, options.cbdir) # Print the banner. for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) # bannerFormat = "{:<18} {:<24}" bannerFormat = " {} {}" log.info( bannerFormat.format( "Crossbar.io Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) if pubkey: log.info( bannerFormat.format("Node Public Key:", click.style(pubkey, fg='yellow', bold=True))) log.info() log.info("Running from node directory '{cbdir}'", cbdir=options.cbdir) from twisted.python.reflect import qual log.info("Controller process starting ({python}-{reactor}) ..", python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1]) from crossbar.controller.node import Node from crossbar.common.checkconfig import InvalidConfigException # represents the running Crossbar.io node # node = Node(options.cbdir, reactor=reactor) # check and load the node configuration # try: if options.config: # load node config from file node.load(options.config) elif options.cdc: # load built-in CDC config node.load() else: # no config file, and not running CDC mode raise Exception( "Neither a node config was found, nor CDC mode is active.") except InvalidConfigException as e: log.error("Invalid node configuration") log.error("{e!s}", e=e) sys.exit(1) except: raise # now actually start the node .. # def start_crossbar(): d = node.start(cdc_mode=options.cdc) def on_error(err): log.error("{e!s}", e=err.value) log.error("Could not start node") if reactor.running: reactor.stop() d.addErrback(on_error) reactor.callWhenRunning(start_crossbar) # enter event loop # try: reactor.run() except Exception: log.failure("Could not start reactor - {log_failure.value}")
def run_command_start(options, reactor=None): """ Subcommand "crossbar start". """ assert reactor # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'wb') as fd: argv = options.argv options_dump = vars(options) pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': {x: y for x, y in options_dump.items() if x not in ["func", "argv"]} } fd.write("{}\n".format( json.dumps( pid_data, sort_keys=False, indent=4, separators=(', ', ': '), ensure_ascii=False ) ).encode('utf8')) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) log = make_logger() # possibly generate new node key # from crossbar.controller.node import maybe_generate_key pubkey = maybe_generate_key(log, options.cbdir) # Print the banner. for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) # bannerFormat = "{:<18} {:<24}" bannerFormat = " {} {}" log.info(bannerFormat.format("Crossbar.io Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) if pubkey: log.info(bannerFormat.format("Node Public Key:", click.style(pubkey, fg='yellow', bold=True))) log.info() log.info("Running from node directory '{cbdir}'", cbdir=options.cbdir) from twisted.python.reflect import qual log.info("Controller process starting ({python}-{reactor}) ..", python=platform.python_implementation(), reactor=qual(reactor.__class__).split('.')[-1]) from crossbar.controller.node import Node from crossbar.common.checkconfig import InvalidConfigException # represents the running Crossbar.io node # node = Node(options.cbdir, reactor=reactor) # check and load the node configuration # try: if options.config: # load node config from file node.load(options.config) elif options.cdc: # load built-in CDC config node.load() else: # no config file, and not running CDC mode raise Exception("Neither a node config was found, nor CDC mode is active.") except InvalidConfigException as e: log.error("Invalid node configuration") log.error("{e!s}", e=e) sys.exit(1) except: raise # now actually start the node .. # def start_crossbar(): d = node.start(cdc_mode=options.cdc) def on_error(err): log.error("{e!s}", e=err.value) log.error("Could not start node") if reactor.running: reactor.stop() d.addErrback(on_error) reactor.callWhenRunning(start_crossbar) # enter event loop # try: reactor.run() except Exception: log.failure("Could not start reactor - {log_failure.value}")
def run_command_start(options, reactor=None): """ Subcommand "crossbar start". """ assert reactor # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'wb') as fd: argv = options.argv options_dump = vars(options) pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': {x: y for x, y in options_dump.items() if x not in ["func", "argv"]} } fd.write("{}\n".format( json.dumps( pid_data, sort_keys=False, indent=3, separators=(', ', ': '), ensure_ascii=False ) ).encode('utf8')) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) log = make_logger() # Print the banner. for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) bannerFormat = "{:>12} {:<24}" log.info(bannerFormat.format("Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) # log.info(bannerFormat.format("Python:", click.style(platform.python_implementation(), fg='yellow', bold=True))) # log.info(bannerFormat.format("Reactor:", click.style(qual(reactor.__class__).split('.')[-1], fg='yellow', bold=True))) # log.info(bannerFormat.format("Started:", click.style(utcnow(), fg='yellow', bold=True))) log.info() log.info("Starting from node directory {}".format(options.cbdir)) from crossbar.controller.node import Node from crossbar.common.checkconfig import InvalidConfigException node = Node(reactor, options) try: node.check_config() except InvalidConfigException as e: log.error("*** Configuration validation failed ***") log.error("{e!s}", e=e) sys.exit(1) except: raise def start_crossbar(): """ Start the crossbar node. """ d = node.start() def on_error(err): log.error("Could not start node: {error}", error=err.value) if reactor.running: reactor.stop() d.addErrback(on_error) reactor.callWhenRunning(start_crossbar) try: log.info("Entering reactor event loop...") reactor.run() except Exception: log.failure("Could not start reactor: {log_failure.value}")
def run_command_start(options): """ Subcommand "crossbar start". """ # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format(json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) # we use an Autobahn utility to import the "best" available Twisted reactor # reactor = install_reactor(options.reactor, options.debug) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) # start Twisted logging # if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath(os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format("Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 20 + " Crossbar.io " + "=" * 20 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format(platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) # create and start Crossbar.io node # from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
def run_command_start(options): """ Subcommand "crossbar start". """ # do not allow to run more than one Crossbar.io instance # from the same Crossbar.io node directory # pid_data = check_is_running(options.cbdir) if pid_data: print("Crossbar.io is already running from node directory {} (PID {}).".format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format(json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) # we use an Autobahn utility to import the "best" available Twisted reactor # reactor = install_reactor(options.reactor, options.debug) # remove node PID file when reactor exits # def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) # start Twisted logging # from crossbar._logging import LogLevel, start_logging if options.logdir: # We want to log to a file from crossbar._logging import make_legacy_daily_logfile_observer log_publisher.addObserver( make_legacy_daily_logfile_observer(options.logdir, options.loglevel)) else: # We want to log to stdout/stderr. from crossbar._logging import make_stdout_observer from crossbar._logging import make_stderr_observer if options.loglevel == "none": # Do no logging! pass elif options.loglevel == "quiet": # Quiet: Only print warnings and errors to stderr. log_publisher.addObserver(make_stderr_observer( (LogLevel.warn, LogLevel.error, LogLevel.critical), show_source=False, format=options.logformat)) elif options.loglevel == "standard": # Standard: For users of Crossbar log_publisher.addObserver(make_stdout_observer( (LogLevel.info,), show_source=False, format=options.logformat)) log_publisher.addObserver(make_stderr_observer( (LogLevel.warn, LogLevel.error, LogLevel.critical), show_source=False, format=options.logformat)) elif options.loglevel == "verbose": # Verbose: for developers # Adds the class source. log_publisher.addObserver(make_stdout_observer( (LogLevel.info, LogLevel.debug), show_source=True, format=options.logformat)) log_publisher.addObserver(make_stderr_observer( (LogLevel.warn, LogLevel.error, LogLevel.critical), show_source=True, format=options.logformat)) elif options.loglevel == "trace": # Verbose: for developers # Adds the class source + "trace" output log_publisher.addObserver(make_stdout_observer( (LogLevel.info, LogLevel.debug), show_source=True, format=options.logformat, trace=True)) log_publisher.addObserver(make_stderr_observer( (LogLevel.warn, LogLevel.error, LogLevel.critical), show_source=True, format=options.logformat)) else: assert False, "Shouldn't ever get here." # Actually start the logger. start_logging() for line in BANNER.splitlines(): log.info(click.style(("{:>40}").format(line), fg='yellow', bold=True)) bannerFormat = "{:>12} {:<24}" log.info(bannerFormat.format("Version:", click.style(crossbar.__version__, fg='yellow', bold=True))) # log.info(bannerFormat.format("Python:", click.style(platform.python_implementation(), fg='yellow', bold=True))) # log.info(bannerFormat.format("Reactor:", click.style(qual(reactor.__class__).split('.')[-1], fg='yellow', bold=True))) log.info(bannerFormat.format("Started:", click.style(utcnow(), fg='yellow', bold=True))) log.info() log.info("Starting from node directory {}".format(options.cbdir)) # create and start Crossbar.io node # from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.info("Entering reactor event loop...") reactor.run() except Exception: log.failure("Could not start reactor: {log_failure.value}")
def run_command_start(options): """ Subcommand "crossbar start". """ ## do not allow to run more than one Crossbar.io instance ## from the same Crossbar.io node directory ## pid_data = check_is_running(options.cbdir) if pid_data: print( "Crossbar.io is already running from node directory {} (PID {}).". format(options.cbdir, pid_data['pid'])) sys.exit(1) else: fp = os.path.join(options.cbdir, _PID_FILENAME) with open(fp, 'w') as fd: argv = options.argv options_dump = vars(options) del options_dump['func'] del options_dump['argv'] pid_data = { 'pid': os.getpid(), 'argv': argv, 'options': options_dump } fd.write("{}\n".format( json.dumps(pid_data, sort_keys=False, indent=3, separators=(',', ': ')))) ## we use an Autobahn utility to import the "best" available Twisted reactor ## reactor = install_reactor(options.reactor, options.debug) ## remove node PID file when reactor exits ## def remove_pid_file(): fp = os.path.join(options.cbdir, _PID_FILENAME) if os.path.isfile(fp): os.remove(fp) reactor.addSystemEventTrigger('after', 'shutdown', remove_pid_file) ## start Twisted logging ## if not options.logdir: logfd = sys.stderr else: from twisted.python.logfile import DailyLogFile logfd = DailyLogFile.fromFullPath( os.path.join(options.logdir, 'node.log')) from crossbar.twisted.processutil import DefaultSystemFileLogObserver flo = DefaultSystemFileLogObserver(logfd, system="{:<10} {:>6}".format( "Controller", os.getpid())) log.startLoggingWithObserver(flo.emit) log.msg("=" * 20 + " Crossbar.io " + "=" * 20 + "\n") import crossbar log.msg("Crossbar.io {} starting".format(crossbar.__version__)) from twisted.python.reflect import qual log.msg("Running on {} using {} reactor".format( platform.python_implementation(), qual(reactor.__class__).split('.')[-1])) log.msg("Starting from node directory {}".format(options.cbdir)) ## create and start Crossbar.io node ## from crossbar.controller.node import Node node = Node(reactor, options) node.start() try: log.msg("Entering reactor event loop ...") reactor.run() except Exception as e: log.msg("Could not start reactor: {0}".format(e))
class CrossbarRouter(object): serializers = [u'msgpack'] def __init__(self, host='localhost', port=61000, realm=u'golem', datadir=None, crossbar_dir='crossbar', crossbar_log_level='trace'): if datadir: self.working_dir = os.path.join(datadir, crossbar_dir) else: self.working_dir = crossbar_dir if not os.path.exists(self.working_dir): os.makedirs(self.working_dir) if not os.path.isdir(self.working_dir): raise IOError("'{}' is not a directory".format(self.working_dir)) self.address = WebSocketAddress(host, port, realm) self.log_level = crossbar_log_level self.node = None self.pubkey = None self.options = self._build_options() self.config = self._build_config(self.address, self.serializers) logger.debug('xbar init with cfg: %s', self.config) def start(self, reactor, callback, errback): reactor.callWhenRunning(self._start, self.options, reactor, callback, errback) @inlineCallbacks def stop(self): yield self.node._controller.shutdown() def _start(self, options, reactor, callback, errback): self._start_node(options, reactor).addCallbacks(callback, errback) def _start_node(self, options, reactor): self.node = Node(options.cbdir, reactor=reactor) self.node.log = LoggerBridge() self.pubkey = self.node.maybe_generate_key(options.cbdir) checkconfig.check_config(self.config) self.node._config = self.config return self.node.start() def _build_options(self, argv=None, config=None): return CrossbarRouterOptions( cbdir=self.working_dir, logdir=None, loglevel=self.log_level, argv=argv, config=config ) @staticmethod def _build_config(address, serializers, allowed_origins=u'*', realm=u'golem', enable_webstatus=False): return { 'version': 2, 'workers': [{ 'type': u'router', 'options': { 'title': u'Golem' }, 'transports': [ { 'type': u'websocket', 'serializers': serializers, 'endpoint': { 'type': u'tcp', 'interface': unicode(address.host), 'port': address.port }, 'url': unicode(address), 'options': { 'allowed_origins': allowed_origins, 'enable_webstatus': enable_webstatus, } } ], 'components': [], "realms": [{ "name": realm, "roles": [{ "name": u'anonymous', "permissions": [{ "uri": u'*', "allow": { "call": True, "register": True, "publish": True, "subscribe": True } }] }] }], }] }
class CrossbarRouter(object): serializers = ['msgpack'] @enum.unique class CrossbarRoles(enum.Enum): admin = enum.auto() docker = enum.auto() # pylint: disable=too-many-arguments def __init__(self, datadir: str, host: str = CROSSBAR_HOST, port: int = CROSSBAR_PORT, realm: str = CROSSBAR_REALM, crossbar_log_level: str = 'info', ssl: bool = True, generate_secrets: bool = False) -> None: self.working_dir = os.path.join(datadir, CROSSBAR_DIR) os.makedirs(self.working_dir, exist_ok=True) if not os.path.isdir(self.working_dir): raise IOError("'{}' is not a directory".format(self.working_dir)) self.cert_manager = cert.CertificateManager(self.working_dir) if generate_secrets: self.cert_manager.generate_secrets() self.address = WebSocketAddress(host, port, realm, ssl) self.log_level = crossbar_log_level self.node = None self.pubkey = None self.options = self._build_options() self.config = self._build_config(self.address, self.serializers, self.cert_manager) logger.debug('xbar init with cfg: %s', json.dumps(self.config)) def start(self, reactor, options=None): # imports reactor from crossbar.controller.node import Node, default_native_workers options = options or self.options if self.address.ssl: self.cert_manager.generate_if_needed() self.node = Node(options.cbdir, reactor=reactor) self.pubkey = self.node.maybe_generate_key(options.cbdir) workers = default_native_workers() checkconfig.check_config(self.config, workers) self.node._config = self.config return self.node.start() @inlineCallbacks def stop(self): yield self.node._controller.shutdown() # noqa # pylint: disable=protected-access def _build_options(self, argv=None, config=None): return CrossbarRouterOptions( cbdir=self.working_dir, logdir=None, loglevel=self.log_level, argv=argv, config=config ) @staticmethod def _users_config(cert_manager: cert.CertificateManager): # configuration for crsb_users with admin priviliges admin_role: str = CrossbarRouter.CrossbarRoles.admin.name docker_role: str = CrossbarRouter.CrossbarRoles.docker.name user_roles = { cert_manager.CrossbarUsers.golemapp: admin_role, cert_manager.CrossbarUsers.golemcli: admin_role, cert_manager.CrossbarUsers.electron: admin_role, cert_manager.CrossbarUsers.docker: docker_role, } crsb_users = {} for user, role in user_roles.items(): entry = {} entry['secret'] = cert_manager.get_secret(user) entry['role'] = role crsb_users[user.name] = entry return crsb_users @staticmethod def _build_config(address: WebSocketAddress, serializers: Iterable[str], cert_manager: cert.CertificateManager, realm: str = CROSSBAR_REALM, enable_webstatus: bool = False): allowed_origins = [ 'http://' + address.host + ':*', 'https://' + address.host + ':*' ] ws_endpoint = { 'type': 'tcp', 'interface': address.host, 'port': address.port, } if address.ssl: ws_endpoint["tls"] = { "key": cert_manager.key_path, "certificate": cert_manager.cert_path, "dhparam": cert_manager.dh_path, } return { 'version': 2, 'controller': { 'options': { 'shutdown': ['shutdown_on_shutdown_requested'] } }, 'workers': [{ 'type': 'router', 'options': { 'title': 'Golem' }, 'transports': [{ 'type': 'websocket', 'serializers': serializers, 'endpoint': ws_endpoint, 'url': str(address), 'options': { 'allowed_origins': allowed_origins, 'enable_webstatus': enable_webstatus, }, "auth": { "wampcra": { "type": "static", "users": CrossbarRouter._users_config(cert_manager) } } }], 'components': [], "realms": [{ "name": realm, "roles": [ { "name": CrossbarRouter.CrossbarRoles.admin.name, "permissions": [{ "uri": '*', "allow": { "call": True, "register": True, "publish": True, "subscribe": True } }] }, { "name": CrossbarRouter.CrossbarRoles.docker.name, "permissions": [ { "uri": '*', "allow": { "call": False, "register": False, "publish": False, "subscribe": False } }, { # more specific config takes precedence "uri": f'{DOCKER_URI}.*', "allow": { "call": True, "register": False, "publish": False, "subscribe": False } }, { "uri": 'sys.exposed_procedures', "allow": { "call": True, "register": False, "publish": False, "subscribe": False }, }, ] }] }], }] }