def command_response(body, message): """ Process a command received from an actuator :param body: command body :param message: complete message (headers, meta, ...) :return: None """ log.info(msg=f'Message response received: {body}') headers = getattr(message, "headers", {}) actuator = None if headers.get('error', False): correlation_ID = headers['source'].get('correlationID', '') opts = { '_coap_id' if isHex(correlation_ID) else 'command_id': correlation_ID } command = get_or_none(SentHistory, **opts) log.error(msg=f'Message Failure: cmd - {command.command_id}, {body}') response = {'error': body} else: act_host, act_port = headers.get('socket', '').split(':')[0:2] correlation_ID = headers.get('correlationID', '') opts = { '_coap_id' if isHex(correlation_ID) else 'command_id': correlation_ID } command = get_or_none(SentHistory, **opts) profile = headers.get('profile', '') encode = headers.get('encode', 'json') response = decode_msg(body, encode) actuator = get_or_none( model=Actuator, profile__iexact=profile, device__transport__host__iexact=act_host, device__transport__port=safe_cast(act_port, int), device__transport__protocol=get_or_none(Protocol, name__iexact=headers.get( 'transport', ''))) if hasattr(actuator, '__iter__'): log.warn( msg= f'Multiple actuators match for command response - {command.command_id}' ) actuator = random.choice(actuator) try: cmd_rsp = ResponseHistory(command=command, actuator=actuator, response=response) cmd_rsp.save() except Exception as e: log.error(msg=f'Message response failed to save: {e}')
def vocab_import(request): bad_vocabs = [] if request.method == 'POST': data = request.FILES.get('json').read() data_parse = json.loads(data) # list_screens = data_parse.keys() print(data_parse) for screen_code in data_parse: screen = get_or_none(Screen, screen_code=screen_code) if not screen: screen = create_screen_from_code_only(screen_code=screen_code, user=request.user) list_words = data_parse[screen_code] for word in list_words: vocab = get_or_none(Vocabulary, vocab_key=word) if vocab: if screen not in vocab.screen.all(): vocab.screen.add(screen) else: bad_vocabs.append(vocab) else: obj = Vocabulary.objects.create( vocab_key=word, english_definition=list_words[word].get('en', ''), vn_definition=list_words[word].get('vn', ''), korean_definition=list_words[word].get('kr', ''), created_by=request.user, modified_by=request.user) obj.screen.add(screen) obj.save() return render(request, 'vocabulary/import.html', {'bad_vocabs': bad_vocabs})
def update(self, request, order_id=None): order = get_or_none(Order, id=order_id) if order is None: return Response({ 'error': 'Order does not exist.', 'updated': False }) data = dict() data = request.data.dict() pizza_obj = get_or_none(Pizza, id=request.data.get('pizza', None)) customer_obj = get_or_none(Customer, id=request.data.get('customer', None)) if pizza_obj is not None: data.update({'pizza': pizza_obj}) if customer_obj is not None: data.update({'customer': customer_obj}) try: order = self.queryset.filter(id=order_id).update(**data) return Response({'updated': True if order is not None else False}) except Exception as e: return Response({ 'error': str(e), 'updated': False, })
def create(self, request): try: pizza_obj = get_or_none(Pizza, id=request.data.get('pizza', None)) customer_obj = get_or_none(Customer, id=request.data.get('customer', None)) size = int(request.data.get('size')) if size < 30 or size > 50: raise Exception('Invalid size. Must be beetwen 30 and 50 cms.') data = dict() data.update({ 'pizza': pizza_obj, 'customer': customer_obj, 'size': size, 'customer_address': request.data.get('customer_address', ''), }) order = self.queryset.create(**data) serializer = OrderSerializer(order) return Response({ 'data': serializer.data, 'created': True, }) except Exception as e: return Response({ 'error': str(e), 'created': False, })
def check_command_id(sender, instance=None, **kwargs): """ Validate the command id given is a UUID :param sender: sender instance - SentHistory :param instance: SENDER instance :param kwargs: key/value args :return: None """ if instance.command_id is None: log.info( msg=f"Command submitted without command id, command id generated") instance.command_id = uuid.uuid4() instance.command.update({"id": str(instance.command_id)}) else: try: val = uuid.UUID(str(instance.command_id), version=4) except ValueError: log.info(msg=f"Invalid command id received: {instance.command_id}") raise ValueError("Invalid command id") tmp = get_or_none(sender, command_id=val) if val is None and tmp is not None: log.info( msg=f"Duplicate command id received: {instance.command_id}") raise ValueError("command id has been used")
def list(self, request, *args, **kwargs): # pylint: disable=unused-argument """ Return a list of a users command history """ username = kwargs.get('username', None) self.pagination_class.page_size_query_param = 'length' self.pagination_class.max_page_size = 100 queryset = self.filter_queryset(self.get_queryset()) username = bleach.clean(username) if request.user.is_staff: # Admin User user = get_or_none(User, username=username) if user is None: raise Http404 queryset = queryset.filter(user=user) else: # Standard User if request.user.username == username: queryset = queryset.filter(user=request.user) else: raise PermissionDenied page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
def _val_channel(self, act: Actuator): if isinstance(act, list) and len(act) == 1: act = act[0] if isinstance(act, Actuator): proto = self._channel.get("protocol", None) if proto: dev = get_or_none(Device, device_id=act.device.device_id) proto = get_or_none(dev.transport, protocol__name=bleach.clean(str(proto))) proto = proto.protocol if proto else None serial = self._channel.get("serialization", None) if serial: serial = get_or_none(Serialization, name=bleach.clean(str(serial))) return proto, serial return None, None
def _val_actuator(self) -> Union[Tuple[dict, int], Tuple[Tuple[dict, int], str], Tuple[List[Actuator], str]]: if self._actuator is None: # TODO: Actuator broadcast?? log.error(usr=self._usr, msg="User attempted to send to a null actuator") return dict( detail="actuator invalid", response="Actuator Invalid: actuator cannot be none" ), 400 act_arr = self._actuator.split("/", 1) if len(act_arr) != 2: log.error(usr=self._usr, msg=f"User attempted to send to an invalid actuator - {self._actuator}") return dict( detail="actuator invalid", response="Actuator Invalid: application error" ), 400 act_type, act = act_arr act_type = bleach.clean(str(act_type)) act = bleach.clean(str(act).replace("_", " ")) if act_type == "actuator": # Single Actuator actuators = get_or_none(Actuator, actuator_id=act) rtn = [actuators, ] if actuators is None: rtn = dict( detail="actuator invalid", response="Actuator Invalid: actuator must be specified with a command" ), 404 return rtn, 'device' if act_type == "profile": # Profile Actuators print(f'Profile: {act}') actuators = get_or_none(ActuatorProfile, name__iexact=act) if actuators is None: return dict( detail="profile cannot be found", response="Profile Invalid: profile must be a valid registered profile with the orchestrator" ), 400 return list(Actuator.objects.filter(profile__iexact=act.replace(" ", "_"))), 'profile' return dict( detail="actuator invalid", response="Actuator Invalid: application error" ), 400
def validate_actuator(usr, act=""): if act is None: # TODO: actuator broadcast?? log.error(usr=usr, msg="User attempted to send to a null actuator") return dict(detail="actuator invalid", response="Actuator Invalid: actuator cannot be none"), 400 act_type = act.split("/", 1) if len(act_type) != 2: log.error(usr=usr, msg=f"User attempted to send to an invalid actuator - {act}") return dict(detail="actuator invalid", response="Actuator Invalid: application error"), 400 _type, _act_prof = act_type _type = bleach.clean(str(_type)) _act_prof = bleach.clean(str(_act_prof).replace("_", " ")) if _type == "actuator": # Single Actuator actuators = get_or_none(Actuator, actuator_id=_act_prof) if actuators is None: return dict( detail="actuator invalid", response= "Actuator Invalid: actuator must be specified with a command" ), 404 return [ actuators, ] elif _type == "profile": # Profile Actuators actuators = get_or_none(ActuatorProfile, name__iexact=_act_prof) if actuators is None: return dict( detail=f"profile cannot be found", response= f"Profile Invalid: profile must be a valid registered profile with the orchestrator" ), 400 return list( Actuator.objects.filter( profile__iexact=_act_prof.replace(" ", "_"))) else: return dict(detail="actuator invalid", response="Actuator Invalid: application error"), 400
def validate_channel(act, chan={}): if len(act) == 1: act = act[0] if isinstance(act, Actuator): dev = get_or_none(Device, device_id=act.device.device_id) proto = chan.get("protocol", None) if proto: proto = get_or_none(dev.transport, protocol__name=bleach.clean(str(proto))) proto = proto.protocol if proto else None serial = chan.get("serialization", None) if serial: serial = get_or_none(Serialization, name=bleach.clean(str(serial))) return proto, serial return None, None
def save(self, *args, **kwargs): """ Override the save function for added validation :param args: save args :param kwargs: save key/value args :return: None """ if not self.protocol.pub_sub: trans = get_or_none(Transport, host=self.host, port=self.port, protocol=self.protocol) trans = trans if isinstance(trans, (list, QuerySet)) else [trans] if len(trans) > 1: raise DjangoValidationError("host, port, and protocol must make a unique pair unless a pub/sub protocol") super(Transport, self).save(*args, **kwargs)
def destroy(self, request, order_id=None): order = get_or_none(Order, id=order_id) if order is None: return Response({ 'error': 'Order does not exist.', 'deleted': False }) try: order.delete() return Response({ 'deleted': True, }) except Exception as e: return Response({ 'error': str(e), })
def verify_unique(sender, instance=None, **kwargs): """ On Device transport change, check the updated transport is unique :param sender: sender instance - Device :param instance: SENDER instance :param kwargs: key/value args :return: None """ action = kwargs.get("action", None) transports = [get_or_none(Transport, pk=t) for t in kwargs.get("pk_set", [])] transports = list(filter(None, transports)) for trans in transports: count = trans.device_set.count() if action == "pre_add" and count > 1: raise IntegrityError("Transport cannot be associated with more that one device") if action in ("post_clear", "post_remove") and count == 0: trans.delete()
def action_send(usr=None, cmd={}, actuator="", channel={}): """ Process a command prior to sending it to the specified actuator(s)/profile :param usr: user sending command :param cmd: OpenC2 command :param actuator: actuator/profile receiving command :param channel: serialization & protocol to send the command :return: response dict """ err = validate_usr(usr) if err: return err err = validate_cmd(usr, cmd) if err: return err actuators = None err = validate_actuator(usr, actuator) if err: if isinstance(err, (Actuator, list)): actuators = err else: return err protocol, serialization = validate_channel(actuators, channel) # Store command in db cmd_id = cmd.get("id", uuid.uuid4()) if get_or_none(SentHistory, command_id=cmd_id): return dict(command_id=["This ID is used by another command."]), 400 else: com = SentHistory(command_id=cmd_id, user=usr, command=cmd) try: com.save() except ValueError as e: return dict(detail="command error", response=str(e)), 400 orc_ip = global_preferences.get("orchestrator__host", "127.0.0.1") orc_id = global_preferences.get("orchestrator__id", "") # Process Actuators that should receive command processed_acts = set() # Process Protocols protocols = [protocol] if protocol else Protocol.objects.all() for proto in protocols: proto_acts = [ a for a in actuators if a.device.transport.filter(protocol__name=proto.name).exists() ] proto_acts = list( filter(lambda a: a.id not in processed_acts, proto_acts)) processed_acts.update({act.id for act in proto_acts}) if len(proto_acts) >= 1: if proto.name.lower() == "coap" and com.coap_id is b"": corr_id = com.gen_coap_id() com.save() else: corr_id = str(com.command_id) header = dict(source=dict( orchestratorID=orc_id, transport=dict(type=proto.name, socket=f"{orc_ip}:{proto.port}"), correlationID=corr_id, date=f"{com.received_on:%a, %d %b %Y %X %Z}"), destination=[]) for act in proto_acts: com.actuators.add(act) trans = act.device.transport.filter( protocol__name=proto.name).first() encoding = (serialization if serialization else trans.serialization.first()).name.lower() dev = list( filter( lambda d: d["deviceID"] == str(act.device.device_id), header["destination"])) profile = str(act.profile).lower() if len(dev) == 1: idx = header["destination"].index(dev[0]) header["destination"][idx]["profile"].append(profile) else: dest = dict(deviceID=str(act.device.device_id), socket=f"{trans.host}:{trans.port}", profile=[profile], encoding=encoding) if trans.protocol.pub_sub: print("PUB SUB") header["destination"].append(dest) # Send command to transport log.info( usr=usr, msg= f"Send command {com.command_id}/{com.coap_id.hex()} to buffer") settings.MESSAGE_QUEUE.send(msg=json.dumps(cmd), headers=header, routing_key=proto.name.lower().replace( " ", "_")) wait = safe_cast(global_preferences.get("command__wait", 1), int, 1) rsp = None for _ in range(wait): rsp = get_or_none(ResponseHistory, command=com) if rsp: break else: time.sleep(1) rsp = [r.response for r in rsp] if hasattr(rsp, "__iter__") else ( [rsp.response] if hasattr(rsp, "response") else None) return dict(detail=f"command {'received' if rsp is None else 'processed'}", response=rsp if rsp else "pending", command_id=com.command_id, command=com.command, wait=wait), 200
def action_send(usr: User, cmd: dict, actuator: str, channel: dict): """ Process a command prior to sending it to the specified actuator(s)/profile :param usr: user sending command :param cmd: OpenC2 command :param actuator: actuator/profile receiving command :param channel: serialization & protocol to send the command :return: response Tuple(dict, int) """ val = Validator(usr, cmd, actuator, channel) acts, protocol, serialization = val.validate() (actuators, fmt) = acts # Store command in db if "id" in cmd: cmd_id = cmd.get("id", uuid.uuid4()) try: cmd_id = uuid.UUID(cmd_id, version=4) except ValueError: cmd_id = uuid.uuid4() cmd["id"] = str(cmd_id) else: cmd_id = uuid.uuid4() if get_or_none(SentHistory, command_id=cmd_id): return dict( command_id=[ "This ID is used by another command." ] ), 400 com = SentHistory(command_id=cmd_id, user=usr, command=cmd) try: com.save() except ValueError as e: return dict( detail="command error", response=str(e) ), 400 # Process Actuators that should receive command processed_acts = set() # Process Protocols for proto in [protocol] if protocol else Protocol.objects.all(): proto_acts = [a for a in actuators if a.device.transport.filter(protocol__name=proto.name).exists()] proto_acts = list(filter(lambda a: a.id not in processed_acts, proto_acts)) processed_acts.update({act.id for act in proto_acts}) if len(proto_acts) >= 1: if proto.name.lower() == "coap" and com.coap_id == b"": com.gen_coap_id() com.save() # Send command to transport log.info(usr=usr, msg=f"Send command {com.command_id}/{com.coap_id.hex()} to buffer") settings.MESSAGE_QUEUE.send( msg=json.dumps(cmd), headers=get_headers(proto, com, proto_acts, serialization, fmt), routing_key=proto.name.lower().replace(" ", "_") ) wait = safe_cast(global_preferences.get("command__wait", 1), int, 1) rsp = None for _ in range(wait): rsp = get_or_none(ResponseHistory, command=com) if rsp: break time.sleep(1) rsp = [r.response for r in rsp] if hasattr(rsp, "__iter__") else ([rsp.response] if hasattr(rsp, "response") else None) return dict( detail=f"command {'received' if rsp is None else 'processed'}", response=rsp if rsp else "pending", command_id=com.command_id, command=com.command, wait=wait ), 200
def retrieve(self, request, order_id=None): order = get_or_none(Order, id=order_id) if order is None: return Response({'error': 'Order does not exist.'}) serializer = OrderSerializer(order) return Response({'data': serializer.data})