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']]
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
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)
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()
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"]
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)
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(), }
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)
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"]
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]
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")
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)
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']
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
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)
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})
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()), })
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"])
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()
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
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
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)
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
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']
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 }] }
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)
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)
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)
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)
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]
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()), } )
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)
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...???
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})
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 }
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)
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']
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]