Example #1
0
    def create_from_request(self, request=None, data=None):
        """
        Creates a new session.
        :param request:
        :return:
        """
        if data is None:
            data = {}
        if 'gateway_id' not in data or data['gateway_id'] is None:
            data['gateway_id'] = self.gateway_id
        if 'id' not in data or data['id'] is None:
            data['id'] = random_string(length=randint(30, 35))
        if 'session_data' not in data:
            data['session_data'] = {}

        if request is not None:
            request.addCookie(self.config.cookie_session_name,
                              data['id'],
                              domain=self.get_cookie_domain(request),
                              path=self.config.cookie_path,
                              max_age=self.config.max_session,
                              secure=self.config.secure,
                              httpOnly=self.config.httponly)

        self.active_sessions[data['id']] = Auth(self, data)
        return self.active_sessions[data['id']]
Example #2
0
 def __init__(self,
              crontab_callback,
              min=allMatch,
              hour=allMatch,
              day=allMatch,
              month=allMatch,
              dow=allMatch,
              label='',
              enabled=True,
              crontab_library=None,
              args=(),
              kwargs={}):
     """
     Setup the cron event.
     """
     self.crontab_library = crontab_library
     self.cron_task_id = random_string(length=10)
     self.crontab_callback = crontab_callback
     self.mins = conv_to_set(min)
     self.hours = conv_to_set(hour)
     self.days = conv_to_set(day)
     self.months = conv_to_set(month)
     self.dow = conv_to_set(dow)
     self.label = label
     self.enabled = enabled
     self.args = args
     self.kwargs = kwargs
Example #3
0
    def publish(self,
                topic,
                message,
                qos=0,
                priority=10,
                retain=False,
                republish=None):
        """
        Publish a message.

        :param topic: 'yombo/devices/bedroom_light/command'
        :param message: string - Like 'on'
        :param qos: 0, 1, or 2. Default is 0.
        :return:
        """
        job = {
            'type': 'publish',
            'topic': topic,
            'message': message,
            'qos': qos,
            'retain': retain,
            'priority': priority,
            'published': False,
        }
        if republish is None or republish is True:
            self.republish_queue[random_string()] = job
            self.dispatch_job_queue()
        else:
            self.queue.put(job, priority=priority)
Example #4
0
 def rotate_username(self):
     configs = yield self.read_configs()
     if configs is False:
         self.generate_configs()
     else:
         myStr = random_string(length=12, char_set='0123456789ABCDEF')
         self.username = '******'.join(myStr[i:i + 2]
                                  for i in range(0, len(myStr), 2))
         self.pin_code = "%s-%s-%s" % (
             random_string(length=3, char_set='0123456789'),
             random_string(length=2, char_set='0123456789'),
             random_string(length=3, char_set='0123456789'))
         self.config_file['bridge']['username'] = self.username
         self.config_file['bridge']['pin'] = self.pin_code
     yield self.save_configs()
     self.restart_homebridge()
Example #5
0
    def ttl(self, ttl: Optional[int] = None, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int] = None) -> TTLCache:
        """
        Create a new TTL based cache. Items in this cache will timeout after a certain period
        of time.

        Default is 120 seconds timeout with a maxsize of 512 entires.

        :param ttl: seconds cache should be good for.
        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if ttl is None:
            ttl = 120
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)

        if name not in self.caches:
            self.caches[name] = {
                "cache": TTLCache(maxsize, ttl),
                "tags": tags,
                "type": "TTLCache",
                "lock": RLock(),
            }
        return self.caches[name]["cache"]
Example #6
0
    def amqp_incoming_response(self, headers, body, properties, **kwargs):
        """
        Process configuration information coming from Yombo Servers. After message is validated, the payload is
        delivered here.

        This is an intermediary method and converts the msg into usable chunks and delievered to "add_update_delete".
        In the future, these may be delievered to the individual libraries for processing.

        :param msg: raw message from AQMP
        :param properties: msg properties, so additional information can be retrieved.
        :return:
        """
        # logger.info("properties: {properties}", properties=properties)
        # logger.info("headers: {headers}", headers=properties.headers)

        response_type = headers["response_type"]
        if response_type not in CONFIG_ITEMS:
            raise YomboWarning(
                f"Configuration item 'get_{response_type}' not configured.")
        self.__process_queue[random_string(length=10)] = {
            "msg": body,
            "headers": headers,
            "properties": properties,
        }

        full_response_type = "get_" + response_type
        if full_response_type not in self.__pending_updates:  # Ignore old request items.
            logger.warn(
                "Configuration item not requested, dropping: {response_type}",
                response_type=response_type)
            return
        self.__pending_updates[full_response_type]["status"] = "received"
        self.process_config_queue()
def return_good(request,
                data_type,
                id=None,
                attributes=None,
                included=None,
                code=None,
                meta=None):
    request.setHeader("Content-Type", CONTENT_TYPE_JSON)

    if id is None:
        id = random_string(length=15)
    if attributes is None:
        attributes = {}

    response = {
        "type": data_type,
        "id": id,
        "attributes": attributes,
    }
    if included is not None:
        response["included"] = included
    if meta is not None:
        response["meta"] = meta

    return return_good_base(request,
                            data=response,
                            included=included,
                            code=code,
                            meta=meta)
Example #8
0
    def lfu(self, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int]=None) -> LFUCache:
        """
        Create a new LFU (Least Frequently Used) based cache. This counts how often
        the cache items are used, and when the maxsize is reached, it will discard
        the least freqently used item. Use this with caution, new items tend to be
        pushed off faster and older but less freqently used items. See LRU cache.

        Default is a maxsize of 512 entires.

        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)
        if name not in self.caches:
            self.caches[name] = {
                "cache": LFUCache(maxsize),
                "tags": tags,
                "type": "LFUCache",
                "lock": RLock(),
            }
Example #9
0
    def __init__(self, parent, device, data, source=None):
        logger.debug("Creating new Device_State... {data}", data=data)

        self._internal_label = "device_states"  # Used by mixins
        self._can_have_fake_data = True
        super().__init__(parent)

        self.device = device
        self.command = None

        # database fields
        if "state_id" in data:
            self.state_id = data["state_id"]
            del data["state_id"]
        else:
            self.state_id = random_string(length=20)
        self.request_id = None
        self.set_at = time()
        self.energy_usage = None
        self.energy_type = None
        self.human_state = None
        self.human_message = None
        self.machine_state = None
        self.machine_state_extra = None
        self.auth_id = None
        self.requesting_source = None
        self.reporting_source = None
        self.uploaded = None
        self.uploadable = None

        self.update_attributes(data, source=source)
Example #10
0
    def lru(self, tags: Optional[Union[list, str]] = None, name: Optional[str] = None,
            maxsize: Optional[int] = None) -> LRUCache:
        """
        Create a new LRU (least recently used) based cache. This cache discards the
        least recently used items to make space if needed.

        Default is a maxsize of 512 entires.

        :param ttl: seconds cache should be good for.
        :param tags: Associate tags with this cache for purging.
        :param name: Name of the cache.
        :param maxsize: Max number of entries.
        :return:
        """
        if maxsize is None:
            maxsize = 512

        if isinstance(tags, str):
            tags = (tags,)
        elif tags is None:
            tags = ()

        if name is None:
            name = caller_string() + random_string(length=10)

        if name not in self.caches:
            self.caches[name] = {
                "cache": LRUCache(maxsize),
                "tags": tags,
                "type": "LRUCache",
                "lock": RLock(),
            }
        return self.caches[name]["cache"]
Example #11
0
    def create(self, request=None, make_active=None, session_data=None, gateway_id=None):
        """
        Creates a new session.
        :param request:
        :param make_active: If True or None (default), then store sesion in memory.
        :return:
        """
        if session_data is None:
            session_data = {}
        if 'gateway_id' in session_data:
            gateway_id = session_data['gateway_id']
            if gateway_id is None:
                gateway_id = self.gateway_id
                session_data['gateway_id'] = gateway_id
        else:
            session_data['gateway_id'] = self.gateway_id

        if 'id' in session_data:
            session_id = session_data['id']
            del session_data['id']
        else:
            session_id = random_string(length=randint(19, 25))

        if gateway_id is None:
            gateway_id = self.gateway_id
            session_data['gateway_id'] = gateway_id

        if request is not None:
            request.addCookie(self.config.cookie_session_name, session_id, domain=self.get_cookie_domain(request),
                              path=self.config.cookie_path, max_age=self.config.max_session,
                              secure=self.config.secure, httpOnly=self.config.httponly)

        if make_active is True or make_active is None:
            self.active_sessions[session_id] = Session(self, session_id, gateway_id, session_data)
            return self.active_sessions[session_id]
Example #12
0
    def encode_message(self, destination_id=None, component_type=None, component_name=None,
                       payload=None, reply_to=None):
        """
        Creates a basic dictionary to represent the message and then pickles it using JSON.

        :param payload: Dictionary to send.
        :param destination_id:
        :return:
        """
        if destination_id is None:
            destination_id = "all"

        message_id = random_string(length=20)
        body = {
            "payload": payload,
            "time_sent": time(),
            "source_id": self.gateway_id,
            "destination_id": destination_id,
            "message_id": message_id,
            "component_type": component_type,
            "component_name": component_name,
            "reply_to": reply_to,
            "protocol_version": 2,
            }
        message_out = {
            "body": data_pickle(body, "msgpack_base85"),
        }
        message_out["hash"] = sha256_compact(message_out["body"])
        message = {
            "body": body,
            "hash": message_out["hash"],
        }
        return message_id, message, data_pickle(message_out, "msgpack_base85")
Example #13
0
    def publish(self,
                topic,
                message,
                qos=0,
                priority=10,
                retain=False,
                republish=None):
        """
        Publish a message.

        :param topic: "yombo/devices/bedroom_light/command"
        :param message: string - Like "on"
        :param qos: 0, 1, or 2. Default is 0.
        :return:
        """
        job = {
            "type": "publish",
            "topic": topic,
            "message": message,
            "qos": qos,
            "retain": retain,
            "priority": priority,
            "published": False,
        }
        if republish is None or republish is True:
            self.republish_queue[random_string()] = job
            self.dispatch_job_queue()
        else:
            self.queue.put(job, priority=priority)
Example #14
0
    def add_notice(self,
                   notice,
                   from_db=False,
                   persist=True,
                   create_event=False):
        """
        Add a new notice.

        :param notice: A dictionary containing notification details.
        :type record: dict
        :returns: Pointer to new notice. Only used during unittest
        """
        print("adding notice1: %s" % notice)
        if 'id' not in notice:
            notice['id'] = random_string(length=16)
        if 'type' not in notice:
            notice['type'] = 'system'
        if 'priority' not in notice:
            notice['priority'] = 'normal'
        if 'source' not in notice:
            notice['source'] = ''

        if 'expire' not in notice:
            if 'timeout' in notice:
                notice['expire'] = int(time()) + notice['timeout']
            else:
                notice['expire'] = int(time()) + 3600
        else:
            if notice['expire'] > int(time()):
                YomboWarning(
                    "New notification is set to expire before current time.")
        if 'created_at' not in notice:
            notice['created_at'] = int(time())

        if 'acknowledged' not in notice:
            notice['acknowledged'] = False
        else:
            if notice['acknowledged'] not in (True, False):
                YomboWarning(
                    "New notification 'acknowledged' must be either True or False."
                )

        if 'title' not in notice:
            raise YomboWarning("New notification requires a title.")
        if 'message' not in notice:
            raise YomboWarning("New notification requires a message.")
        if 'meta' not in notice:
            notice['meta'] = {}

        logger.debug("notice: {notice}", notice=notice)
        if from_db is False:
            self._LocalDB.add_notification(notice)
            self.notifications.prepend(notice['id'], Notification(notice))
        else:
            self.notifications[notice['id']] = Notification(notice)
            # self.notifications = OrderedDict(sorted(self.notifications.items(), key=lambda x: x[1]['created_at']))
            pass
        return notice['id']
Example #15
0
    def check_destination(self, destination, file_id, mangle_name):
        """
        Checks the destinations and returns three items within a list:
        1) urlparsed results from the destination
        2) urlparse results from destination meant for thumbnails.
        3) an extra random id that is used to further hide the URL of the resource.

        :param destination:
        :param file_id:
        :param mangle_name:
        :return:
        """
        # print(f"storage: save file dest: {destination}")
        # print(f"storage: manglename: {mangle_name}")
        mangle_id = self._Hash.sha224_compact(random_string(length=100))[0:20]
        if mangle_name > 0:
            folder, filename = ntpath.split(destination)
            file, extension = path.splitext(filename)
            if mangle_name == 1:
                destination = f"{folder}/{file}_{file_id}_{mangle_id}{extension}"
                destination_thumb = f"{folder}/{file}_{file_id}_{mangle_id}_thumb{extension}"
            if mangle_name == 2:
                destination = f"{folder}/{file_id}_{mangle_id}{extension}"
                destination_thumb = f"{folder}/{file_id}_{mangle_id}_thumb{extension}"
            if mangle_name == 3:
                destination = f"{folder}/{file_id}_{mangle_id}"
                destination_thumb = f"{folder}/{file_id}_{mangle_id}_thumb"
            if mangle_name == 4:
                new_filename = self._Hash.sha256_compact(
                    random_string(length=200))
                destination = f"{folder}/{new_filename}{extension}"
                destination_thumb = f"{folder}/{new_filename}{extension}_thumb"
            if mangle_name >= 5:
                new_filename = self._Hash.sha256_compact(
                    random_string(length=200))
                destination = f"{folder}/{new_filename}"
                destination_thumb = f"{folder}/{new_filename}_thumb"

            # print(f"storage: save file new dest: {destination}")
        dest_parts = urlparse(destination)
        scheme = dest_parts.scheme
        if scheme not in self.storage:
            raise KeyError(f"Unknown file storage location type: {scheme}")
        dest_parts_thumb = urlparse(destination_thumb)
        return dest_parts, dest_parts_thumb, mangle_id
Example #16
0
 def _set_new_auth_id(self):
     """ Sets the auth_id and possibly the auth_key. """
     auth_key_id_full = random_string(length=AUTHKEY_ID_LENGTH_FULL)
     self.auth_key_id = self._Hash.sha256_compact(auth_key_id_full)
     if self.preserve_key is True:
         self.auth_key_id_full = auth_key_id_full
     else:
         self.auth_key_id_full = ""
     return auth_key_id_full
    def rotate(self):
        """
        Rotates the authkey ID. It's a good idea to rotate keys regularly.

        :return:
        """
        old_auth_id = self.auth_id
        self.auth_id = random_string(length=randint(50, 55))
        self._Parent.finish_rotate_key(old_auth_id, self.auth_id, self)
Example #18
0
    def generate_key(self, request_id=None):
        """
        Generates a new GPG key pair. Updates yombo.ini and marks it to be sent when gateway conencts to server
        again.
        """
        if self.gateway_id is 'local' or self.gwuuid is None:
            self._key_generation_status[
                request_id] = 'failed-gateway not setup'
            return
        input_data = self.gpg.gen_key_input(name_email=self.gwuuid() +
                                            "@yombo.net",
                                            name_real="Yombo Gateway",
                                            name_comment="gw_" + self.gwuuid(),
                                            key_type='RSA',
                                            key_length=2048,
                                            expire_date='5y')

        if request_id is None:
            request_id = random_string(length=16)
        self._key_generation_status[request_id] = 'working'
        newkey = yield self.gpg.gen_key(input_data)
        self._key_generation_status[request_id] = 'done'

        # print("newkey!!!!!! ====")
        # print(format(newkey))
        # print("request id =")
        # print(request_id)
        if newkey == '':
            logger.error(
                "ERROR: Unable to generate GPG keys.... Is GPG installed and configured? Is it in your path?"
            )
            raise YomboCritical(
                "Error with python GPG interface.  Is it installed?")

        private_keys = self.gpg.list_keys(True)
        keyid = ''

        for key in private_keys:
            if str(key['fingerprint']) == str(newkey):
                keyid = key['keyid']
        asciiArmoredPublicKey = self.gpg.export_keys(keyid)

        gpg_keys = yield self.gpg.list_keys(keyid)
        keys = self._format_list_keys(gpg_keys)

        # print("keys: %s" % type(keys))
        # print("keys: %s" % keys)

        data = keys[format(newkey)]
        data['publickey'] = asciiArmoredPublicKey
        data['notes'] = 'Key generated during wizard setup.'
        data['have_private'] = 1
        yield self.local_db.insert_gpg_key(data)

        returnValue({'keyid': keyid, 'keypublicascii': asciiArmoredPublicKey})
Example #19
0
        def apiv1_system_tools_ping(webinterface, request):
            try:
                request_id = request.args.get('id')[0]
            except Exception as e:
                request_id = random_string(length=12)

            return return_good(request,
                               payload={
                                   'id': request_id,
                                   'time': float(time()),
                               })
Example #20
0
 def _init_(self, **kwargs):
     """Sets up the default hasher."""
     self.keys = {
         "main": {
             "salt": self._Configs.get("hashids.main",
                                       random_string(length=25)),
             "length": "20",
         },
     }
     for key_id, key in self.keys.items():
         key["hasher"] = Hashids(salt=key["salt"], min_length=key["length"])
Example #21
0
        def apiv1_stream_get(webinterface, request, session):
            print("Got a new stream.  Request: %s" % request)
            request.setHeader('Content-type', 'text/event-stream')
            request.write(EventMsg(int(time()), 'ping'))

            # We'll want to write more things to this client later, so keep the request
            # around somewhere.
            webinterface.api_stream_spectators[random_string(length=10)] = request

            # Indicate we're not done with this request by returning a deferred.
            # (In fact, this deferred will never fire, which is kinda fishy of us.)
            return defer.Deferred()
Example #22
0
 def _init_(self, **kwargs):
     """
     Get the encryption system up - load the or create a default master key.
     """
     aes_key_path = f"{self._working_dir}/etc/gpg/aes.key"
     try:
         self.__aes_key = yield self._Files.read(aes_key_path,
                                                 convert_to_unicode=False)
     except FileNotFoundError:
         self.__aes_key = random_string(length=512)
         yield self._Files.save(aes_key_path, self.__aes_key)
     self.__aes_key = self.__aes_key
Example #23
0
    def generate_message_request(self, exchange_name, source, destination, headers, body, callback=None):
        new_body = {
            "data_type": "object",
            "request"  : body,
        }
        if isinstance(body, list):
            new_body['data_type'] = 'objects'

        request_msg = self.generate_message(exchange_name, source, destination, "request",
                                            headers, new_body, callback=callback)
        request_msg['properties']['correlation_id'] = random_string(length=16)
        # request_msg['properties']['headers']['request_type']=request_type
        return request_msg
Example #24
0
 def page_gpg_keys_generate_key(webinterface, request, session):
     request_id = random_string(length=16)
     #        self._Libraries['gpg'].generate_key(request_id)
     page = webinterface.get_template(
         request, webinterface._dir +
         'pages/configs/gpg_generate_key_started.html')
     webinterface.home_breadcrumb(request)
     webinterface.add_breadcrumb(request, "/gpg/index", "GPG Keys")
     webinterface.add_breadcrumb(request, "/gpg/generate_key",
                                 "Generate Key")
     return page.render(request_id=request_id,
                        getattr=getattr,
                        type=type)
Example #25
0
    def rotate(self, auth_id):
        """
        Rotates an API Auth key for security.

        :return:
        """
        auth = self.get(auth_id)
        old_auth_id = auth.auth_id
        auth.auth_id = random_string(length=randint(50, 55))
        yield self._LocalDB.rotate_api_auth(old_auth_id, auth.auth_id)

        self.active_api_auth_by_label[auth.label] = auth.auth_id
        return auth
Example #26
0
    def add_notice(self, notice, from_db=False, persist=True, create_event=False):
        """
        Add a new notice.

        :param notice: A dictionary containing notification details.
        :type record: dict
        :returns: Pointer to new notice. Only used during unittest
        """
        print "adding notice1: %s" % notice
        if 'id' not in notice:
            notice['id'] = random_string(length=16)
        if 'type' not in notice:
            notice['type'] = 'system'
        if 'priority' not in notice:
            notice['priority'] = 'normal'
        if 'source' not in notice:
            notice['source'] = ''

        if 'expire' not in notice:
            if 'timeout' in notice:
                notice['expire'] = int(time()) + notice['timeout']
            else:
                notice['expire'] = int(time()) + 3600
        else:
            if notice['expire'] > int(time()):
                YomboWarning("New notification is set to expire before current time.")
        if 'created' not in notice:
            notice['created'] = int(time())

        if 'acknowledged' not in notice:
            notice['acknowledged'] = False
        else:
            if notice['acknowledged'] not in (True, False):
                YomboWarning("New notification 'acknowledged' must be either True or False.")

        if 'title' not in notice:
            raise YomboWarning("New notification requires a title.")
        if 'message' not in notice:
            raise YomboWarning("New notification requires a message.")
        if 'meta' not in notice:
            notice['meta'] = {}

        logger.debug("notice: {notice}", notice=notice)
        if from_db is False:
            self._LocalDB.add_notification(notice)
            self.notifications.prepend(notice['id'], Notification(notice))
        else:
            self.notifications[notice['id']] = Notification(notice)
            # self.notifications = OrderedDict(sorted(self.notifications.iteritems(), key=lambda x: x[1]['created']))
            pass
        return notice['id']
Example #27
0
 def generate_configs(self):
     myStr = random_string(length=12, char_set='0123456789ABCDEF')
     self.username = '******'.join(myStr[i:i + 2]
                              for i in range(0, len(myStr), 2))
     self.pin_code = "%s-%s-%s" % (
         random_string(length=3, char_set='0123456789'),
         random_string(length=2, char_set='0123456789'),
         random_string(length=3, char_set='0123456789'))
     self.config_file = {
         "bridge": {
             "name": "Yombo Homebridge",
             "username": self.username,
             "port": 51826,
             "pin": self.pin_code
         },
         "description":
         "Basic configuration for use with Yombo gateways.",
         "platforms": [{
             "platform":
             "Yombo",
             "name":
             "Yombo",
             "host":
             "http://localhost:8080",
             "apiauth":
             "",
             "supported_types": [
                 "automation", "binary_sensor", "climate", "cover",
                 "device_tracker", "fan", "group", "input_boolean", "light",
                 "lock", "media_player", "remote", "scene", "script",
                 "sensor", "switch", "vacuum"
             ],
             "logging":
             True,
             "verify_ssl":
             True
         }]
     }
Example #28
0
    def generate_message_response(self, properties, exchange_name, source, destination, headers, body ):
        response_msg = self.generate_message(exchange_name, source, destination, "response", headers, body)
        if properties.correlation_id:
           response_msg['properties']['correlation_id'] = properties.correlation_id
#        response_msg['properties']['headers']['response_type']=response_type
        correlation_id = random_string(length=12)

        print "properties: %s" % properties
        if 'route' in properties.headers:
            route = str(properties.headers['route']) + ",yombo.gw.amqpyombo:" + self.user_id
            response_msg['properties']['headers']['route'] = route
        else:
            response_msg['properties']['headers']['route'] = "yombo.gw.amqpyombo:" + self.user_id
        return response_msg
        def page_gpg_keys_generate_key(webinterface, request, session):
            session.has_access("system_setting", "*", "view")

            request_id = random_string(length=16)
            page = webinterface.get_template(
                request, webinterface.wi_dir +
                "/pages/configs/gpg_generate_key_started.html")
            webinterface.home_breadcrumb(request)
            webinterface.add_breadcrumb(request, "/gpg/index", "GPG Keys")
            webinterface.add_breadcrumb(request, "/gpg/generate_key",
                                        "Generate Key")
            return page.render(request_id=request_id,
                               getattr=getattr,
                               type=type)
Example #30
0
def update_request(request, api):
    """
    Modifies the request to add "received_cookies in unicode. Also, adds a "args"
    attribute that contains the incoming arguments, but in unicode. Also adds "_" to the
    templates, but it for the current user's language.

    Finally, it adds cache-control and expires items to ensure the content isn't cached.

    :param request:
    :return:
    """
    request.auth = None
    if api in ("true", True, 1, "yes"):
        request.api = True
    else:
        request.api = False

    request.received_cookies = bytes_to_unicode(request.received_cookies)
    request.args = bytes_to_unicode(request.args)
    request.request_id = random_string(length=25)
    request.setHeader("Cache-Control", "no-cache, no-store, must-revalidate")  # don't cache!
    request.setHeader("Expires", "-1")  # don't cache!

    # Make uniform arguments available as 'processed_arguments'. First, if the request is type
    # POST, PATCH, or PUT, then first try to decode the body request. If not, then use the query string args.

    request.processed_body = None
    request.processed_body_encoding = None

    if bytes_to_unicode(request.method).lower() in ("post", "patch", "put"):
        content_type = bytes_to_unicode(request.getHeader("content-type"))
        if isinstance(content_type, str):
            content_type = content_type.lower()
        else:
            content_type = ""

        if content_type == CONTENT_TYPE_JSON:
            try:
                request.processed_body = bytes_to_unicode(json.loads(request.content.read()))
                request.processed_body_encoding = "json"
            except Exception as e:
                logger.info("Error decoding web request 'json' data: {e}", e=e)
        elif content_type == CONTENT_TYPE_MSGPACK:
            try:
                request.processed_body = bytes_to_unicode(msgpack.unpackb(request.content.read()))
                request.processed_body_encoding = "msgpack"
            except Exception as e:
                logger.info("Error decoding web request 'msgpack' data: {e}", e=e)
    common_headers(request)
Example #31
0
    def setup_defaults(self) -> None:
        """
        Setups some basic configurations. Very helpful for new gateways or reinstalls until a recovered
        yombo.toml can be uploaded/installed.
        """
        if self.get("core.first_run", None) is None:
            self.set("core.first_run", True)

        # Allows gateway control commands from yombo servers.
        self.get("security.amqp.allow_system_control", True)
        # Allows remove device control from yombo servers. If this is disabled, commands cannot be relayed if remove
        # devices cannot directly access the gateway.
        self.get("security.amqp.allow_device_control", True)
        # Send device states to yombo servers. Used by user's remote devices to get device states.
        self.get("security.amqp.send_device_states", True)
        # Send private statistics. This doesn't provice much details, mostly usage details.
        self.get("security.amqp.send_private_stats", True)
        # Send public statistics. Nothing relating to the user's activities and devices.
        self.get("security.amqp.send_anon_stats", True)

        self.get("core.gwid", "local")
        self.get("core.is_master", True)
        self.get("core.master_gateway_id", "local")
        self.get("core.rand_seed", random_string(length=80))
        self.get("core.system_user_prefix", "gw")

        if self.get("database.type", "sqlite") == "sqlite":
            self.get("database.path", f"{self._working_dir}/etc/yombo.sqlite3")

        # set system defaults. Reasons: 1) All in one place. 2) Somes values are needed before respective libraries
        # are loaded.
        self.get("mqttyombo.enabled", True)
        self.get("mosquitto.enabled", True)
        self.get("mosquitto.max_connections", 1000)
        self.get("mosquitto.timeout_disconnect_delay", 2)
        self.get("mosquitto.server_listen_ip", "*")
        self.get("mosquitto.server_listen_port", 1883)
        self.get("mosquitto.server_listen_port_ss_ssl", 1884)
        self.get("mosquitto.server_listen_port_le_ssl", 1885)
        self.get("mosquitto.server_listen_port_websockets", 8081)
        self.get("mosquitto.server_listen_port_websockets_ss_ssl", 8444)
        self.get("mosquitto.server_listen_port_websockets_le_ssl", 8445)
        self.get("mosquitto.server_allow_anonymous", False)
        self.get("misc.temperature_display", "f")
        self.get("misc.length_display",
                 "imperial")  # will we ever get to metric?

        self.get("webinterface.nonsecure_port", 8080)
        self.get("webinterface.secure_port", 8443)
Example #32
0
    def remote_get_key(self, key_hash, request_id=None):
        """
        Send a request to Yombo server to fetch a key.

        :param keyHash:
        :return:
        """
        if request_id is None:
            request_id = random_string()
        if self._Loader.check_component_status('AMQPYombo', '_start_'):
            content = {'id': key_hash}
            amqp_message = self._AMQP.generate_message_request(
                'ysrv.e.gw_config', 'yombo.gateway.lib.gpg',
                'yombo.server.configs', content, self.amqp_response_get_key)
            self._AMQP.publish(amqp_message)
Example #33
0
    def create(self, request):
        """
        Creates a new session.
        :param request:
        :return:
        """

        session_id = random_string(length=20)
        self.active_sessions[session_id] = Session()
        self.active_sessions[session_id].init(self, session_id)

        request.addCookie(self.config.cookie_session, session_id, domain=self.config.cookie_domain,
                          path=self.config.cookie_path, max_age=self.config.max_session,
                          secure=self.config.secure, httpOnly=self.config.httponly)

        return self.active_sessions[session_id]
Example #34
0
        def apiv1_system_tools_ping(webinterface, request, session):
            """
            Responds to a simple ping. This allows frontend client to judge how far away the gateway is.
            """
            try:
                request_id = request.args.get("id")[0]
            except Exception as e:
                request_id = random_string(length=12)

            return webinterface.render_api(request, session,
                                           data_type="system_ping",
                                           attributes={
                                               "id": request_id,
                                               "time": float(time()),
                                               }
                                           )
Example #35
0
 def __init__(self, crontab_callback, min=allMatch, hour=allMatch, day=allMatch,
                    month=allMatch, dow=allMatch, label='',
                    enabled=True, crontab=None, args=(), kwargs={}):
     """
     Setup the cron event.
     """
     self.crontab_callback = crontab_callback
     self.mins = conv_to_set(min)
     self.hours= conv_to_set(hour)
     self.days = conv_to_set(day)
     self.months = conv_to_set(month)
     self.dow = conv_to_set(dow)
     self.label = label
     self.enabled = enabled
     self.crontab = crontab
     self.args = args
     self.kwargs = kwargs
     self.cron_id = random_string(length=10)
Example #36
0
    def __init__(self, loader):  # we do some simulation of a Yombo Library...
        self.loader = loader
        self._FullName = "yombo.gateway.lib.webinterface.sessions"
        self._Configs = self.loader.loadedLibraries['configuration']

        self.config = DictObject({
            'cookie_session': 'yombo_' + self._Configs.get('webinterface', 'cookie_session', random_string(length=randint(60,80))),
            'cookie_pin': 'yombo_' + self._Configs.get('webinterface', 'cookie_pin', random_string(length=randint(60,80))),
            'cookie_domain': None,
            'cookie_path' : '/',
            'max_session': 15552000,  # How long session can be good for: 180 days
            'max_idle': 5184000,  # Max idle timeout: 60 days
            'max_session_no_auth': 600,  # If not auth in 10 mins, delete session
            'ignore_expiry': True,
            'ignore_change_ip': True,
            'expired_message': 'Session expired',
            'httponly': True,
            'secure': False,  # will change to true after SSL system/dns complete. - Mitch
        })
        self.localdb = self.loader.loadedLibraries['localdb']
        self.active_sessions = {}
        self.active_sessions_cache = ExpiringDict(200, 5)  # keep 200 entries, for at most 1 second...???
Example #37
0
    def generate_key(self, request_id = None):
        """
        Generates a new GPG key pair. Updates yombo.ini and marks it to be sent when gateway conencts to server
        again.
        """
        input_data = self.gpg.gen_key_input(
            name_email=self.gwuuid + "@yombo.net",
            name_real="Yombo Gateway",
            name_comment="gw_" + self.gwuuid,
            key_type='RSA',
            key_length=2048,
            expire_date='5y')

        if request_id is None:
            request_id = random_string(length=16)
        self._key_generation_status[request_id] = 'working'
        newkey = yield self.gpg.gen_key(input_data)
        self._key_generation_status[request_id] = 'done'

        print "newkey!!!!!! ===="
        print newkey
        print "request id ="
        print request_id
        if newkey == '':
            print "\n\rERROR: Unable to generate GPG keys.... Is GPG installed and configured? Is it in your path?\n\r"
            myExit()

        private_keys = self.gpg.list_keys(True)
        keyid = ''

        for key in private_keys:
            if str(key['fingerprint']) == str(newkey):
                keyid=key['keyid']
        asciiArmoredPublicKey = self.gpg.export_keys(keyid)
        self._Configs.get('gpg', 'keyid', keyid)
        self._Configs.get('gpg', 'keyascii', asciiArmoredPublicKey)
#        sendKey(keyid, asciiArmoredPublicKey)
        print "New keys (public and private) have been saved to key ring."
        returnValue({'keyid': keyid, 'keyascii': asciiArmoredPublicKey})
Example #38
0
    def add_by_string(self, voice_string, call_back = None, device = None, order = 'devicecmd'):
        """
        Adds a voice command by using a string like "desk lamp [on, off]". This will add the following voice commands
        based on the value of 'order'.  If both is provided, the following will be added:

        * desklamp on
        * desklamp off
        * off desklamp
        * on desklamp

        You can specify 'order' as: both, devicecmd, cmddevice. This determines what ordering the voice commands
        are added. In the above example, specifying 'devicecmd', then only the first two items are added.

        Either a callback function must be provided or a device must be provided. Otherwise, the voice command
        will not be added and a YomboWarning will be raised if either or both are defined.

        :raises YomboWarning: If voiceString or destination is invalid.
        :param voice_string: Voice command string to process: "desklamp [on, off]"
        :type voice_string: string
        :param call_back: A function to send the voice command id, device, and command objects to.
        :type call_back: pointer to function
        :param device: A device id or device label to search for a matching device.
        :type device: string
        :param order: The ordering in which to add voice command text lookup. Default: devicecmd
        :type order: string
        """
        logger.debug("Adding voice command: {voice_string}", voice_string=voice_string)
        if call_back is None and device is None:
            raise YomboWarning("'call_back' and 'device' are mising.", 1000, 'add_by_string', 'voicecmds')

        if call_back is not None and device is not None:
            raise YomboWarning("Either specifiy 'call_back' or 'device', not both.", 1001, 'add_by_string', 'voicecmds')

        try:
            tag_re = re.compile('(%s.*?%s)' % (re.escape('['), re.escape(']')))
            string_parts = tag_re.split(voice_string)
            string_parts = filter(None, string_parts) # remove empty bits

            device_obj = None
            if device is not None:
                if isclass(device):
                    device_obj = device
                else:
                    device_obj = self._Devices[device]

            commands = []
        except:
            raise YomboWarning("Invalid format for 'voice_string'", 1010, 'add_by_string', 'voicecmds')


        if len(string_parts) > 2:
            raise YomboWarning("Invalid format for 'voice_string'", 1003, 'add_by_string', 'voicecmds')

        for part in range(len(string_parts)):
            string_parts[part] = string_parts[part].strip()
            if string_parts[part].startswith("[") and string_parts[part].endswith("]"):
                temp_commands = string_parts[part][1:-1].split(',')
                for cmd in range(len(temp_commands)):
                    cmd_temp = temp_commands[cmd].strip()
                    if len(cmd_temp) > 0:
                        commands.append(cmd_temp)
            else:
                device_label = string_parts[part]

        if len(commands) == 0:
            raise YomboWarning("No commands found in voice_string.", 1003, 'add_by_string', 'voicecmds')

        # logger.debug("commands by voice: {commandsByVoice}:{verb}", commandsByVoice=self.commandsByVoice, verb=verb)
        for cmd in commands:
            if cmd not in self.commandsByVoice:
                continue
            command = self.commandsByVoice[cmd]
            vc_id = random_string(length=12)

            if order == 'both':
                self.voice_command_strings["%s %s" % (device_label, cmd)] = vc_id
                self.voice_command_strings["%s %s" % (cmd, device_label)] = vc_id

            elif order == 'devicecmd':
                self.voice_command_strings["%s %s" % (device_label, cmd)] = vc_id

            else:
                self.voice_command_strings["%s %s" % (cmd, device_label)] = vc_id

            self.voice_command_data[vc_id] = {
                'id': vc_id,
                'cmd': command,
                'device': device_obj,
                'call_back': call_back
            }
Example #39
0
    def mqtt_incoming(self, topic, payload, qos, retain):
        """
        Processes incoming MQTT requests. See `MQTT @ Module Development <https://yombo.net/docs/modules/mqtt/>`_

        Examples:

        * /yombo/states/statename/get  - returns a json (preferred)
        * /yombo/states/statename/get abc1234 - returns a json, sends a message ID as a string for tracking
          * A message can only be returned with the above items, cannot be used when requesting a single value.
        * /yombo/states/statename/get/value - returns a string
        * /yombo/states/statename/get/value_type - returns a string
        * /yombo/states/statename/get/value_human - returns a string
        * /yombo/states/statesname/set {"value":"working","value_type":"string"}

        :param topic:
        :param payload:
        :param qos:
        :param retain:
        :return:
        """
        #  0       1       2      3        4
        # yombo/states/statename/get/requested_value
        payload = str(payload)

        parts = topic.split('/', 10)
        # print("Yombo States got this: %s / %s" % (topic, parts))
        # requested_state = urllib.unquote(parts[2])
        requested_state = parts[2].replace("$", ".")
        # requested_state = decoded_state.replace("_", " ")
        if len(parts) <= 3 or len(parts) > 5:
            logger.warn("States received an invalid MQTT topic, discarding. Too long or too short. '%s'" % topic)
            return

        if  parts[3] not in ('get', 'set'):
            # logger.warn("States received an invalid MQTT topic, discarding. Must have either 'set' or 'get'. '%s'" % topic)
            return


        if requested_state not in self.__States:
            self.mqtt.publish('yombo/states/%s/get_response' % parts[2], str('invalid: state not found'))
            return

        state = self.__States[requested_state]

        if parts[3] == 'get':
            request_id = random_string(length=30)

            if len(payload) > 0 and len(payload) < 40:
                if not is_json(payload):
                    request_id = payload

            if len(parts) == 5:
                if parts[4] not in ('value', 'value_type', 'value_human'):
                    logger.warn(
                        "States received an invalid MQTT topic, discarding. '%s'" % topic)
                    return
                self.mqtt.publish('yombo/states/%s/get_response/%s' % (parts[2], parts[4]),
                                  str(state[parts[4]]))
                return

            response = {
                'value': state['value'],
                'value_type': state['value_type'],
                'value_human': state['value_human'],
                'request_id': request_id,
            }
            self.mqtt.publish('yombo/states/%s/get_response' % parts[2],
                              str(response) )
            return

        elif parts[3] == 'set':
            request_id = random_string(length=30)

            if not self.is_json(payload):
                self.mqtt.publish('yombo/states/%s/set_response' % parts[2],
                                  str(
                                      'invalid (%s): Payload must contain json with these: value, value_type, and request_id' % request_id)
                                  )

            data = json.loads(payload)
            if 'request_id' in data:
                request_id = data['request_id']

            if 'value' not in data:
                self.mqtt.publish('yombo/states/%s/set_response' % parts[2],
                                  str(
                                      'invalid (%s): Payload must contain json with these: value, value_type, and request_id' % request_id)
                                  )

            for key in data.keys():
                if key not in ('value', 'value_type', 'request_id'):
                    self.mqtt.publish('yombo/states/%s/set_response' % parts[2],
                          str('invalid (%s): json contents can only contain value, value_type and request_id' %
                              request_id)
                                      )

            if 'value_type' not in data:
                data['value_type'] = None

            self.set(requested_state, value, value_type=data['value_type'], function=None, arguments=None)
Example #40
0
    def add(self, notice, from_db=None, create_event=None):
        """
        Add a new notice.

        :param notice: A dictionary containing notification details.
        :type record: dict
        :returns: Pointer to new notice. Only used during unittest
        """
        if 'title' not in notice:
            raise YomboWarning("New notification requires a title.")
        if 'message' not in notice:
            raise YomboWarning("New notification requires a message.")

        if 'id' not in notice:
            notice['id'] = random_string(length=16)
        else:
            if notice['id'] in self.notifications:
                self.notifications[notice['id']].update(notice)
                return notice['id']

        if 'type' not in notice:
            notice['type'] = 'system'
        if 'priority' not in notice:
            notice['priority'] = 'normal'
        if 'source' not in notice:
            notice['source'] = ''
        if 'always_show' not in notice:
            notice['always_show'] = False
        if 'always_show_allow_clear' not in notice:
            notice['always_show_allow_clear'] = True
        if 'persist' not in notice:
            notice['persist'] = False
        if 'meta' not in notice:
            notice['meta'] = {}

        if notice['persist'] is True and 'always_show_allow_clear' is True:
            YomboWarning("New notification cannot have both 'persist' and 'always_show_allow_clear' set to true.")

        if 'expire' not in notice:
            if 'timeout' in notice:
                notice['expire'] = time() + notice['timeout']
            else:
                notice['expire'] = time() + 3600
        else:
            if notice['expire'] > time():
                YomboWarning("New notification is set to expire before current time.")
        if 'created' not in notice:
            notice['created'] = time()

        if 'acknowledged' not in notice:
            notice['acknowledged'] = 0
        else:
            if notice['acknowledged'] not in (True, False):
                YomboWarning("New notification 'acknowledged' must be either True or False.")

        logger.debug("notice: {notice}", notice=notice)
        if from_db is None and notice['persist'] is True:
            self._LocalDB.add_notification(notice)
            self.notifications.prepend(notice['id'], Notification(self, notice))
        else:
            self.notifications.prepend(notice['id'], Notification(self, notice))
            # self.notifications = OrderedDict(sorted(self.notifications.iteritems(), key=lambda x: x[1]['created']))
            pass
        if from_db is None:
            self.check_always_show_count()
        return notice['id']
Example #41
0
    def new(self, hostname=None, port=5671, virtual_host=None, username=None, password=None, use_ssl=True,
            connected_callback=None, disconnected_callback=None, error_callback=None, client_id=None, keepalive=1800,
            prefetch_count=10):
        """
        Creates a new :py:class:AMQPClient instance. It will not auto-connect, just simply call the connect method
        when you're ready for the instance to start connecting. It will continue to attempt to connect if connection
        is not initially made or connection is dropped. It implements an auto-backup feature so as not to overload
        the server.  For example, it will make connection attempts pretty fast, but will increase the rate of
        connection attempts of time.

        :param hostname: String (required) - IP address or hostname to connect to.
        :param port: Int (required) - Port number to connect to.
        :param virtual_host: String (required) - AMQP virutal host name to connect to.
        :param username: String (Default = None) - Username to connect as. If None, will use the Yombo system username.
          Use "" to not use a username & password.
        :param password: String (Default = None) - Pasword to to connect with. If None, will use the Yombo system
          username. Use "" to not use a password.
        :param use_ssl: bool (Default = True) - Use SSL when attempting to connect to server.
        :param connected_callback: method - If you want a function called when connected to server.
        :param disconnected_callback: method - If you want a function called when disconnected from server.
        :param error_callback: method - A function to call if something goes wrong.
        :param client_id: String (default - random) - A client id to use for logging.
        :param keepalive: Int (default 1800) - How many seconds a ping should be performed if there's not recent
          traffic.
        :param prefetch_count: Int (default 10) - How many outstanding messages the client should have at any
          given time.
        :return:
        """
        if client_id is None:
            client_id = random_string(length=10)

        if client_id in self.client_connections:
            raise YomboWarning ("client_id must be unique. Got: %s" % client_id, 200, 'MQTT::new', 'mqtt')

        if hostname is None:
            raise YomboWarning("New AMQP client must has a servername or IP to connect to.", 200, 'new', 'AMQP')

        if port is None:
            raise YomboWarning("New AMQP client must has a port number to connect to.", 200, 'new', 'AMQP')

        if username is "" or password is "":
            username = None
            password = None

        if virtual_host is None:
            raise YomboWarning("New AMQP client must has a virtual host to connect to.", 200, 'new', 'AMQP')

        if use_ssl is None:
            raise YomboWarning("New AMQP client must have use_ssl set as True or False..", 200, 'new', 'AMQP')

        if connected_callback is not None:
            if callable(connected_callback) is False:
                raise YomboWarning("If incoming_callback is set, it must be be callable.")

        if disconnected_callback is not None:
            if callable(disconnected_callback) is False:
                raise YomboWarning("If incoming_callback is set, it must be be callable.")

        if error_callback is not None:
            if callable(error_callback) is False:
                raise YomboWarning("If error_callback is set, it must be be callable.")

        self.client_connections[client_id] = AMQPClient(self, client_id, hostname, port, virtual_host, username,
                password, use_ssl, connected_callback, disconnected_callback, error_callback, keepalive, prefetch_count)
        return self.client_connections[client_id]