Ejemplo n.º 1
0
    def test_FrozenDict(self):
        d = FrozenDict(cmd_args)
        with self.subTest():
            with self.assertRaises(TypeError):
                d.origin = "ORIGIN"

        with self.subTest():
            self.assertEqual(d.content.action, "deny")
Ejemplo n.º 2
0
 def _subscribe(self, data: FrozenDict) -> None:
     close = set()
     for args in data.values():
         close.add("{host}:{port}".format(**args))
         self._check_subscribe(args)
     if diff := {*self._clients.keys()} - close:
         self.shutdown(list(diff))
Ejemplo n.º 3
0
    def _socketMsg(self, action, act_args, *args, **kwargs):
        auth = kwargs.get("headers", {}).get("Authorization", "")
        token = re.sub(r"^JWT\s+", "", auth) if auth.startswith("JWT") else ""

        url = f"api{act_args['url'].format(*args)}"
        url_params = act_args.get('params', {})
        if len(url_params) > 0:
            url += f"?{'&'.join(f'{k}={v}' for k, v in url_params.items())}"

        rtn = dict(
            body={},
            method=act_args["method"],
            status_code=500,
            url=f"{self._root_url}{url}",
            # Extra Options
            meta={})

        try:
            self._webSocket.send(
                json.dumps(
                    dict(endpoint=url,
                         method=act_args["method"],
                         jwt=token,
                         data=kwargs.get("body", {}),
                         types=dict(
                             success=f"@@socket/{action.upper()}_SUCCESS",
                             failure=f"@@socket/{action.upper()}_FAILURE"))))

            try:
                rslt = json.loads(self._webSocket.recv())
            except ValueError as e:
                rslt = {}

            rtn.update(
                body=rslt.get('payload', {}),
                status_code=safe_cast(
                    rslt.get('meta', {}).get("status_code", 200), int, 200),
                # Extra Options
                meta=rslt.get('meta', {}))
            return FrozenDict(rtn)

        except Exception as e:
            print(e)
            rtn.update(status_code=500, )
            return FrozenDict(rtn)
Ejemplo n.º 4
0
    def __init__(self, root: str = _ROOT_DIR, act_id: str = _ACT_ID) -> None:
        """
        Initialize and start the Actuator Process
        :param root: rood directory of actuator - default CWD
        :param act_id: id of the actuator - default UUIDv4
        """
        config_file = os.path.join(root, "config.json")
        schema_file = os.path.join(root, "schema.json")

        config = general.safe_load(config_file)
        if len({"actuator_id", "schema"} - set(config.keys())) != 0:
            config.setdefault("actuator_id", act_id)
            config.setdefault("schema", general.safe_load(schema_file))
            json.dump(config, open(config_file, "w"), indent=4)

        self._config = FrozenDict(config)
        self._dispatch = dispatch.Dispatch(
            act=self, dispatch_transform=self._dispatch_transform)
        self._dispatch.register(exceptions.action_not_implemented, "default")
        self._pairs = None

        self._valid_actions = ()
        self._valid_targets = ()

        # Get valid Actions & Targets from the schema
        if len({"meta", "types"} -
               set(self._config.schema.keys())) == 0:  # JADN
            self._profile = self._config.schema.get("meta", {}).get(
                "title", "N/A").replace(" ", "_").lower()
            self._validator = None

            for key in ("Action", "Target"):
                key_def = [
                    x for x in self._config.schema.get("types", [])
                    if x[0] == key
                ]
                key_def = key_def[0] if len(key_def) == 1 else None
                if key_def:
                    setattr(self, f"_valid_{key.lower()}s",
                            tuple(a[1] for a in key_def[4]))
                else:
                    raise KeyError(f"{key} not found in schema")
        else:  # JSON
            self._profile = self._config.schema.get("title", "N/A").replace(
                " ", "_").lower()
            self._validator = general.ValidatorJSON(self._config.schema)

            schema_defs = self._config.schema.get("definitions", {})

            self._valid_actions = tuple(
                a["const"]
                for a in schema_defs.get("Action", {}).get("oneOf", []))
            self._valid_targets = tuple(
                schema_defs.get("Target", {}).get("properties", {}).keys())
Ejemplo n.º 5
0
    def _socket(self):
        self._webSocket = websocket.create_connection(self._socket_url,
                                                      timeout=2)
        init_msg = self._webSocket.recv()

        api = dict()
        for name, cls in self._api.items():
            res = {}
            for act, args in getattr(cls, "actions", {}).items():
                res[act] = partial(self._socketMsg, act, args)
            api[name] = FrozenDict(res)
        return api
Ejemplo n.º 6
0
    def _rest(self):
        api = API(
            api_root_url=self._root_url,  # base api url
            headers={  # default headers
                "Content-Type": "application/json"
            },
            timeout=2,  # default timeout in seconds
            append_slash=True,  # append slash to final url
            json_encode_body=True,  # encode body as json
        )
        for name, cls in self._api.items():
            api.add_resource(resource_name=name, resource_class=cls)

        _api = {}
        for resource in api.get_resource_list():
            res = getattr(api, resource)
            _api[resource] = FrozenDict(
                {act: getattr(res, act)
                 for act in res.actions})

        return FrozenDict(_api)
Ejemplo n.º 7
0
 def pairs(self) -> FrozenDict:
     """
     Valid Action/Target pairs registered to this actuator instance
     :return: Action/Target Pairs
     """
     if self._pairs is None:
         pairs = {}
         for p in self._dispatch.registered:
             p = p.split(".")
             if "default" not in p:
                 pairs.setdefault(p[0], []).append(p[1])
         self._pairs = FrozenDict(pairs)
     return self._pairs
Ejemplo n.º 8
0
def mqtt_publish(recipients: List[str], source: dict, device: dict,
                 body: Union[bytes, str], topic: str, auth: Auth,
                 headers: dict) -> None:
    # pylint: disable=unbalanced-tuple-unpacking
    (orc_id, corr_id) = destructure(source, "orchestratorID", "correlationID")
    # pylint: disable=unbalanced-tuple-unpacking
    (fmt, encoding, broker_socket) = destructure(device,
                                                 ("format", "broadcast"),
                                                 ("encoding", "json"),
                                                 ("socket", "localhost:1883"))
    (host, port) = broker_socket.split(":", 1)
    payload = Message(recipients=recipients,
                      origin=f"{orc_id}@{broker_socket}",
                      msg_type=MessageType.Request,
                      request_id=uuid.UUID(corr_id),
                      content_type=SerialFormats(encoding)
                      if encoding in SerialFormats else SerialFormats.JSON,
                      content=json.loads(body))
    print(f"Sending {broker_socket} topic: {topic} -> {payload}")
    publish_props = Properties(PacketTypes.PUBLISH)
    publish_props.PayloadFormatIndicator = int(
        SerialFormats.is_binary(payload.content_type) is False)
    publish_props.ContentType = "application/openc2"  # Content-Type
    publish_props.UserProperty = ("msgType", payload.msg_type)  # User Property
    publish_props.UserProperty = ("encoding", payload.content_type
                                  )  # User Property

    try:
        publish_single(config=FrozenDict(MQTT_HOST=host,
                                         MQTT_PORT=safe_cast(port, int, 1883),
                                         USERNAME=auth.username,
                                         PASSWORD=auth.password,
                                         TLS_SELF_SIGNED=safe_cast(
                                             os.environ.get(
                                                 "MQTT_TLS_SELF_SIGNED", 0),
                                             int, 0),
                                         CAFILE=auth.caCert,
                                         CLIENT_CERT=auth.clientCert,
                                         CLIENT_KEY=auth.clientKey),
                       topic=topic,
                       payload=payload.serialize(),
                       properties=publish_props)
        print(f"Placed payload onto topic {topic} Payload Sent: {payload}")
    except Exception as e:
        print(
            f"There was an error sending command to {broker_socket} topic: {topic} -> {e}"
        )
        send_error_response(e, headers)
Ejemplo n.º 9
0
    def __init__(self, root: str = _ROOT_DIR, act_id: str = _ACT_ID) -> None:
        """
        Initialize and start the Actuator Process
        :param root: rood directory of actuator - default CWD
        :param act_id: id of the actuator - default UUIDv4
        """
        config_file = os.path.join(root, "config.json")
        schema_file = os.path.join(root, "schema.json")

        config = general.safe_load(config_file)
        if "actuator_id" not in config.keys():
            config.setdefault("actuator_id", act_id)
            json.dump(config, open(config_file, "w"), indent=4)

        # Initialize etcd client
        self.etcdClient = etcd.Client(host=os.environ.get('ETCD_HOST', 'etcd'),
                                      port=safe_cast(
                                          os.environ.get('ETCD_PORT', 4001),
                                          int, 4001))

        schema = general.safe_load(schema_file)
        self._config = FrozenDict(**config, schema=schema)
        self._dispatch = dispatch.Dispatch(
            act=self, dispatch_transform=self._dispatch_transform)
        self._dispatch.register(exceptions.action_not_implemented, "default")
        self._pairs = None

        # Get valid Actions & Targets from the schema
        self._profile = self._config.schema.get("title",
                                                "N/A").replace(" ",
                                                               "_").lower()
        self._validator = general.ValidatorJSON(schema)
        schema_defs = self._config.schema.get("definitions", {})

        self._prefix = '/actuator'
        profiles = self.nsid if len(self.nsid) > 0 else [self._profile]

        for profile in profiles:
            self.etcdClient.write(f"{self._prefix}/{profile}",
                                  self._config.actuator_id)

        self._valid_actions = tuple(
            a["const"] for a in schema_defs.get("Action", {}).get("oneOf", []))
        self._valid_targets = tuple(
            schema_defs.get("Target", {}).get("properties", {}).keys())
Ejemplo n.º 10
0
    def __init__(self, root=ROOT_DIR, act_id=ACT_ID, enable_etcd=True) -> None:
        """
        Initialize and start the Actuator Process
        :param root: rood directory of actuator - default CWD
        :param act_id: id of the actuator - default UUIDv4
        """
        config_file = os.path.join(root, "config.json")
        schema_file = os.path.join(root, "schema.json")

        # Set config
        config = general.safe_load(config_file)
        if "actuator_id" not in config.keys():
            config.setdefault("actuator_id", act_id)
            with open(config_file, "w", encoding="UTF-8") as f:
                json.dump(config, f, indent=4)
        schema = general.safe_load(schema_file)
        self._config = FrozenDict(
            **config,
            schema=schema
        )

        # Configure Action/Target functions
        self._dispatch = dispatch.Dispatch(namespace="root",  dispatch_transform=self._dispatch_transform, act=self)
        self._dispatch.register(exceptions.action_not_implemented, "default")

        # Get valid Actions & Targets from the schema
        self._validator = general.ValidatorJSON(schema)
        self._profile = self._config.schema.get("title", "N/A").replace(" ", "_").lower()
        schema_defs = self._config.schema.get("definitions", {})
        self._valid_actions = tuple(a["const"] for a in schema_defs.get("Action", {}).get("oneOf", []))
        self._valid_targets = tuple(schema_defs.get("Target", {}).get("properties", {}).keys())

        # Initialize etcd client and set profiles
        if enable_etcd:
            self._etcd = etcd.Client(
                host=os.environ.get('ETCD_HOST', 'etcd'),
                port=safe_cast(os.environ.get('ETCD_PORT', 4001), int, 4001)
            )
            profiles = self.nsid if len(self.nsid) > 0 else [self._profile]
            for profile in profiles:
                self._etcd.write(f"{self._prefix}/{profile}", self._config.actuator_id)
Ejemplo n.º 11
0
 def update(self, data: FrozenDict) -> None:
     for t_id, args in data.items():
         self._check_subscribe(args)
Ejemplo n.º 12
0
class MessageQueue:
    _auth = FrozenDict({'username': '******', 'password': '******'})
    _exchange = 'orchestrator'
    _consumerKey = 'response'
    _producerExchange = 'producer_transport'

    def __init__(self,
                 hostname='127.0.0.1',
                 port=5672,
                 auth=_auth,
                 exchange=_exchange,
                 consumer_key=_consumerKey,
                 producer_exchange=_producerExchange,
                 callbacks=None):
        """
        Message Queue - holds a consumer class and producer class for ease of use
        :param hostname: server ip/hostname to connect
        :param port: port the AMQP Queue is listening
        :param exchange: name of the default exchange
        :param consumer_key: key to consumer
        :param producer_exchange: ...
        :param callbacks: list of functions to call on message receive
        """
        self._exchange = exchange if isinstance(exchange,
                                                str) else self._exchange
        self._consumerKey = consumer_key if isinstance(
            consumer_key, str) else self._consumerKey
        self._producerExchange = producer_exchange if isinstance(
            producer_exchange, str) else self._producerExchange

        self._publish_opts = dict(host=hostname, port=safe_cast(port, int))

        self._consume_opts = dict(host=hostname,
                                  port=safe_cast(port, int),
                                  exchange=self._exchange,
                                  routing_key=self._consumerKey,
                                  callbacks=callbacks)

        self.producer = Producer(**self._publish_opts)
        self.consumer = Consumer(**self._consume_opts)

    def send(self, msg, headers, exchange=_producerExchange, routing_key=None):
        """
        Publish a message to the specified que and transport
        :param msg: message to be published
        :param headers: header information for the message being sent
        :param exchange: exchange name
        :param routing_key: routing key name
        :return: None
        """
        headers = headers or {}
        if routing_key is None:
            raise ValueError('Routing Key cannot be None')
        self.producer.publish(message=msg,
                              headers=headers,
                              exchange=exchange,
                              routing_key=routing_key)

    def shutdown(self):
        """
        Shutdown the connection to the queue
        """
        self.consumer.shutdown()
        self.consumer.join()
Ejemplo n.º 13
0
import os
from sb_utils import FrozenDict, safe_cast

Config = FrozenDict(
    TLS_ENABLED=os.environ.get('MQTT_TLS_ENABLED', False),
    TLS_SELF_SIGNED=safe_cast(os.environ.get('MQTT_TLS_SELF_SIGNED', 0), int,
                              0),
    CAFILE=os.environ.get('MQTT_CAFILE', None),
    CLIENT_CERT=os.environ.get('MQTT_CLIENT_CERT', None),
    CLIENT_KEY=os.environ.get('MQTT_CLIENT_KEY', None),
    USERNAME=os.environ.get('MQTT_DEFAULT_USERNAME', None),
    PASSWORD=os.environ.get('MQTT_DEFAULT_PASSWORD', None),
    MQTT_PREFIX=os.environ.get('MQTT_PREFIX', ''),
    MQTT_HOST=os.environ.get('MQTT_HOST', 'queue'),
    MQTT_PORT=safe_cast(os.environ.get('MQTT_PORT', 1883), int, 1883),
    # TODO: find alternatives??
    TRANSPORT_TOPICS=[
        t.lower().strip()
        for t in os.environ.get("MQTT_TRANSPORT_TOPICS", "").split(",")
    ],
    TOPICS=[
        t.lower().strip() for t in os.environ.get("MQTT_TOPICS", "").split(",")
    ],
    # ETCD Options
    ETCD_HOST=os.environ.get('ETCD_HOST', 'etcd'),
    ETCD_PORT=safe_cast(os.environ.get('ETCD_PORT', 2379), int, 2379))
Ejemplo n.º 14
0
"""
Query Target functions
"""
from sb_utils import FrozenDict
from sb_utils.actuator import Dispatch, exceptions

Query = Dispatch("query")

Features = FrozenDict(
    pairs=lambda act: act.pairs,
    profiles=lambda act: act.profile,
    rate_limit=lambda act: getattr(act, "rate_limit", 0),
    versions=lambda act: act.schema.get("meta", {}).get("version", "N/A"))


@Query.register
def default(*extra_args, **extra_kwargs):
    return exceptions.target_not_implemented()


@Query.register
def features(act, target=[], args={}, *extra_args, **extra_kwargs):
    if not isinstance(args, dict) and len(set(args) - {"response"}) > 0:
        print("Invalid Query Args")
        return exceptions.bad_argument()

    if not isinstance(target,
                      list) and len(set(target) - set(Features.keys())) > 0:
        return exceptions.bad_request()

    else:
Ejemplo n.º 15
0
from sb_utils import FrozenDict

default_app_config = 'tracking.conf.TrackingConfig'

LEVELS = ('Debug', 'Error', 'Fatal', 'Info', 'Trace', 'Warn')

_DB_LEVELS = tuple((l[0].upper(), l) for l in LEVELS)

EVENT_LEVELS = FrozenDict({l: l[0].upper() for l in LEVELS})

LEVEL_EVENTS = FrozenDict(map(reversed, EVENT_LEVELS.items()))

REQUEST_LEVELS = FrozenDict(Information=range(100, 199),
                            Success=range(200, 299),
                            Redirect=range(300, 399),
                            Client_Error=range(400, 499),
                            Server_Error=range(500, 599))