def createResource(self,
                       resource: Resource,
                       parentResource: Resource = None,
                       originator: str = None) -> Result:
        Logging.logDebug(
            f'Adding resource ri: {resource.ri}, type: {resource.ty:d}')

        if parentResource is not None:
            Logging.logDebug(f'Parent ri: {parentResource.ri}')
            if not parentResource.canHaveChild(resource):
                if resource.ty == T.SUB:
                    err = 'Parent resource is not subscribable'
                    Logging.logWarn(err)
                    return Result(rsc=RC.targetNotSubscribable, dbg=err)
                else:
                    err = f'Invalid child resource type: {T(resource.ty).value}'
                    Logging.logWarn(err)
                    return Result(rsc=RC.invalidChildResourceType, dbg=err)

        # if not already set: determine and add the srn
        if resource.__srn__ is None:
            resource[resource._srn] = Utils.structuredPath(resource)

        # add the resource to storage
        if (res := resource.dbCreate(overwrite=False)).rsc != RC.created:
            return res
 def updateResource(self,
                    resource: Resource,
                    dct: JSON = None,
                    doUpdateCheck: bool = True,
                    originator: str = None) -> Result:
     Logging.logDebug(
         f'Updating resource ri: {resource.ri}, type: {resource.ty:d}')
     if doUpdateCheck:
         if not (res := resource.update(dct, originator)).status:
             return res.errorResult()
	def _copyCSE2CSR(self, target:Resource, source:Resource, isUpdate:bool=False) -> None:

		def _copyAttribute(attr:str) -> None:
			if attr in source and attr not in self.excludeCSRAttributes:
				target[attr] = source[attr]

		if 'csb' in source and 'csb' not in self.excludeCSRAttributes:
			target['csb'] = self.registrarCSEURL
		_copyAttribute('csi')
		_copyAttribute('cst')
		_copyAttribute('csz')
		_copyAttribute('lbl')
		_copyAttribute('nl')
		_copyAttribute('poa')
		_copyAttribute('rr')
		_copyAttribute('srv')
		_copyAttribute('st')
		
		# if 'csi' in source:
		# 	target['csi'] = source.csi
		# if 'cst' in source:
		# 	target['cst'] = source.cst
		# if 'csz' in source:
		# 	target['csz'] = source.csz
		# if 'lbl' in source:
		# 	target['lbl'] = source.lbl
		# if 'nl' in source:
		# 	target['nl'] = source.nl
		# if 'poa' in source:
		# 	target['poa'] = source.poa
		# if 'rr' in source:
		# 	target['rr'] = source.rr
		# if 'srv' in source:
		# 	target['srv'] = source.srv
		# if 'st' in source:
		# 	target['st'] = source.st
		
		if 'cb' not in self.excludeCSRAttributes:
			target['cb'] = f'{source.csi}/{source.rn}'
		if 'dcse' not in self.excludeCSRAttributes:
			target['dcse'] = list(self.descendantCSR.keys())		# Always do this bc it might be different, even empty for an update
		

		# Always remove some attributes
		for attr in [ 'acpi' ]:
			if attr in target:
				target.delAttribute(attr, setNone = False)
		
		# Remove attributes in updates
		if isUpdate:
			# rm some attributes
			for attr in [ 'ri', 'rn', 'ct', 'lt', 'ty', 'cst', 'cb', 'csi']:
				if attr in target:
					target.delAttribute(attr, setNone = False)
    def deleteResource(self,
                       resource: Resource,
                       originator: str = None,
                       withDeregistration: bool = False,
                       parentResource: Resource = None) -> Result:
        Logging.logDebug(
            f'Removing resource ri: {resource.ri}, type: {resource.ty:d}')

        resource.deactivate(originator)  # deactivate it first

        # Check resource deletion
        if withDeregistration:
            if not (res :=
                    CSE.registration.checkResourceDeletion(resource)).status:
                return Result(rsc=RC.badRequest, dbg=res.dbg)
Exemple #5
0
    def _addAnnouncementToResource(self, resource: Resource, dct: JSON,
                                   csi: str) -> None:
        """	Add anouncement information to the resource. These are a list of tuples of 
			the csi to which the resource is registered as well as the ri of the 
			resource on the remote CSE. Also, add the reference in the at attribute.
		"""
        remoteRI = Utils.findXPath(dct, '{0}/ri')
        ats = resource[Resource._announcedTo]
        ats.append((csi, remoteRI))
        resource.setAttribute(Resource._announcedTo, ats)

        # Modify the at attribute
        if len(at := resource.at) > 0 and csi in at:
            at[at.index(
                csi)] = f'{csi}/{remoteRI}'  # replace the element in at
            resource.setAttribute('at', at)
Exemple #6
0
    def updateResourceOnCSR(self, resource: Resource, remoteCSR: Resource,
                            remoteRI: str) -> None:
        """	Update an announced resource to a specific CSR.
		"""

        # retrieve the cse & poas for the remote CSR
        csi, poas = self._getCsiPoaForRemoteCSR(remoteCSR)

        # TODO: multi-hop announcement

        Logging.logDebug(f'Update announced resource: {resource.ri} to: {csi}')

        data = resource.createAnnouncedResourceDict(remoteCSR,
                                                    isCreate=False,
                                                    csi=csi)

        # Get target URL for request
        if poas is not None and len(poas) > 0:
            url = f'{poas[0]}/{remoteRI}'  # TODO check all available poas.
        else:
            Logging.logWarn('Cannot get URL')
            return

        # Create the announed resource on the remote CSE
        Logging.logDebug(f'Updating announced resource at: {csi} url: {url}')
        res = CSE.request.sendUpdateRequest(url, CSE.cseCsi, data=data)
        if res.rsc not in [RC.updated, RC.OK]:
            Logging.logDebug(
                f'Error updating remote announced resource: {res.rsc:d}')
            # Ignore and fallthrough
        Logging.logDebug('Announced resource updated')
Exemple #7
0
    def _removeAnnouncementFromResource(self, resource: Resource,
                                        csi: str) -> None:
        """	Remove announcement details from a resource (internal attribute).
			Modify the internal as well the at attributes to remove the reference
			to the remote CSE.
		"""
        ats = resource[Resource._announcedTo]
        remoteRI: str = None
        for x in ats:
            if x[0] == csi:
                remoteRI = x[1]
                ats.remove(x)
                resource.setAttribute(Resource._announcedTo, ats)

        # # Modify the at attribute
        if remoteRI is not None:
            atCsi = f'{csi}/{remoteRI}'
            if (at := resource.at) is not None and len(at) > 0 and atCsi in at:
                at.remove(atCsi)
                resource.setAttribute('at', at)
Exemple #8
0
    def foptRequest(self,
                    operation: int,
                    fopt: Resource,
                    request: Request,
                    id: str,
                    originator: str,
                    ct: str = None,
                    ty: int = None) -> Tuple[Union[Resource, dict], int, str]:
        """	Handle requests to a fanOutPoint. 
		This method might be called recursivly, when there are groups in groups."""

        # get parent / group and check permissions
        group = fopt.retrieveParentResource()
        if group is None:
            return None, C.rcNotFound, 'group resource not found'

        # get the permission flags for the request operation
        permission = operationsPermissions[operation]

        #check access rights for the originator through memberAccessControlPolicies
        if CSE.security.hasAccess(originator,
                                  group,
                                  requestedPermission=permission,
                                  ty=ty,
                                  isCreateRequest=True if operation
                                  == C.opCREATE else False) == False:
            return None, C.rcOriginatorHasNoPrivilege, 'access denied'

        # get the rqi header field
        _, _, _, rqi, _ = Utils.getRequestHeaders(request)

        # check whether there is something after the /fopt ...
        _, _, tail = id.partition('/fopt/') if '/fopt/' in id else (_, _, '')
        Logging.logDebug('Adding additional path elements: %s' % tail)

        # walk through all members
        result = []
        tail = '/' + tail if len(
            tail) > 0 else ''  # add remaining path, if any
        for mid in group.mid.copy(
        ):  # copy mid list because it is changed in the loop
            # Try to get the SRN and add the tail
            if (srn := Utils.structuredPathFromRI(mid)) is not None:
                mid = srn + tail
            else:
                mid = mid + tail
            # Invoke the request
            if operation == C.opRETRIEVE:
                if (res := CSE.dispatcher.handleRetrieveRequest(
                        request, mid, originator))[0] is None:
                    return res
Exemple #9
0
    def _getAndCheckNUS(self,
                        subscription: Resource,
                        newJson: dict = None,
                        previousNus: List[str] = None,
                        originator: str = None) -> Tuple[List[str], int, str]:
        newNus = []
        if newJson is None:  # If there is no new JSON structure, get the one from the subscription to work with
            newJson = subscription.asJSON()

        # Resolve the URI's in the previousNus.
        if previousNus is not None:
            if (previousNus := self._getNotificationURLs(
                    previousNus, originator)) is None:
                # Fail if any of the NU's cannot be retrieved
                return None, C.rcSubscriptionVerificationInitiationFailed, 'cannot retrieve all previous nu' 's'
Exemple #10
0
        def sender(url: str,
                   serialization: ContentSerializationType,
                   targetResource: Resource = None) -> bool:
            Logging.logDebug(
                f'Sending notification to: {url}, reason: {reason}')
            notificationRequest = {
                'm2m:sgn': {
                    'nev': {
                        'rep': {},
                        'net': NotificationEventType.resourceUpdate
                    },
                    'sur': Utils.fullRI(sub['ri'])
                }
            }
            data = None
            nct = sub['nct']
            nct == NotificationContentType.all and (data := resource.asDict())
            nct == NotificationContentType.ri and (data := {
                'm2m:uri': resource.ri
            })
            nct == NotificationContentType.modifiedAttributes and (data := {
                resource.tpe:
                modifiedAttributes
            })
            # TODO nct == NotificationContentType.triggerPayload

            # Add some values to the notification
            reason is not None and Utils.setXPath(notificationRequest,
                                                  'm2m:sgn/nev/net', reason)
            data is not None and Utils.setXPath(notificationRequest,
                                                'm2m:sgn/nev/rep', data)

            # Check for batch notifications
            if sub['bn'] is not None:

                # TODO implement hasPCH()

                if targetResource is not None and targetResource.hasPCH(
                ):  # if the target resource has a PCH child resource then that will be the target later
                    url = targetResource.ri
                return self._storeBatchNotification(url, sub,
                                                    notificationRequest,
                                                    serialization)
            else:
                return self._sendRequest(url,
                                         notificationRequest,
                                         serialization=serialization,
                                         targetResource=targetResource)
    def handleCreator(self, resource: Resource, originator: str) -> Result:
        """	Check for set creator attribute as well as assign it to allowed resources.
		"""
        if resource.hasAttribute('cr'):
            if resource.ty not in C.creatorAllowed:
                return Result(
                    rsc=RC.badRequest,
                    dbg=
                    f'"creator" attribute is not allowed for resource type: {resource.ty}'
                )
            if resource.cr is not None:  # Check whether cr is set to a value. This is wrong
                Logging.logWarn('Setting "creator" attribute is not allowed.')
                return Result(rsc=RC.badRequest,
                              dbg='setting "creator" attribute is not allowed')
            else:
                resource['cr'] = originator
                # fall-through
        return Result()  # implicit OK
Exemple #12
0
    def _checkNusInSubscription(self,
                                subscription: Resource,
                                newDict: JSON = None,
                                previousNus: list[str] = None,
                                originator: str = None) -> Result:
        """	Check all the notification URI's in a subscription. 
			A verification request is sent to new URI's.
		"""
        newNus = []
        if newDict is None:  # If there is no new resource structure, get the one from the subscription to work with
            newDict = subscription.asDict()

        # Resolve the URI's in the previousNus.
        if previousNus is not None:
            if (previousNus := self._getNotificationURLs(
                    previousNus, originator)) is None:
                # Fail if any of the NU's cannot be retrieved or accessed
                return Result(rsc=RC.subscriptionVerificationInitiationFailed,
                              dbg='cannot retrieve all previous nu\'s')
Exemple #13
0
    def _sendRequest(self,
                     nu: str,
                     ri: str,
                     jsn: dict,
                     reason: int = None,
                     resource: Resource = None,
                     originator: str = None) -> bool:
        Utils.setXPath(jsn, 'm2m:sgn/sur', Utils.fullRI(ri))

        # Add some values to the notification
        # TODO: switch statement:  (x is not None and bla())
        if reason is not None:
            Utils.setXPath(jsn, 'm2m:sgn/nev/net', reason)
        if resource is not None:
            Utils.setXPath(jsn, 'm2m:sgn/nev/rep', resource.asJSON())
        if originator is not None:
            Utils.setXPath(jsn, 'm2m:sgn/cr', originator)

        _, rc, _ = CSE.httpServer.sendCreateRequest(
            nu, Configuration.get('cse.csi'), data=json.dumps(jsn))
        return rc in [C.rcOK]
Exemple #14
0
 def __init__(self, address, name, failure_prob):
     Thread.__init__(self)
     super().__init__(address, name)
     self.resource = Resource(f"{name}-RESOURCE")
     self.interpreter = ArduinoInterpreter(self)
     self.failure_prob = failure_prob
Exemple #15
0
class Arduino(Device, Thread):
    def __init__(self, address, name, failure_prob):
        Thread.__init__(self)
        super().__init__(address, name)
        self.resource = Resource(f"{name}-RESOURCE")
        self.interpreter = ArduinoInterpreter(self)
        self.failure_prob = failure_prob

    def set_resource_on(self):
        self.resource.set_on()

    def set_resource_off(self):
        self.set_resource_off()

    def update_device_keep_alive(self, device, timestamp):
        self.devices_status[device] = timestamp

    def add_new_device(self, new_device, timestamp):
        self.devices_status[new_device] = timestamp

    def update_devices_status_list(self, devices_list):
        self.devices_status.update(devices_list)

    def send_devices_status_list(self, destination):
        message = {
            'action': 'deviceList',
            'origin': self.address,
            'list': self.parse_device_status()
        }
        self.tcp_client.send_message(destination,
                                     self.prepare_message(message))

    def keep_alive_to_all(self):
        message = {
            'action': 'keepAlive',
            'origin': self.address,
            'timestamp': round(time.time())
        }
        device_status_copy = self.devices_status.copy()
        for device in device_status_copy:
            address = (device[0], device[1] + 1)
            self.udp_client.send_message(address,
                                         self.prepare_message(message))
            with open('log', 'a') as log:
                log.write(
                    f'{datetime.now()}: {self.name} (keep-alive) -> {address}\n'
                )

    def call_to_action(self, message, client):
        self.interpreter.interprets_message(message, client)

    def parse_device_status(self):
        parsed = {}
        for device in self.devices_status.keys():
            host, port = device
            key = ":".join([host, str(port)])
            parsed[key] = self.devices_status[device]
        return parsed

    def run(self):
        while True:
            time.sleep(.5)
            self.keep_alive_to_all()
            if random.random() < self.failure_prob:
                with open('log', 'a') as log:
                    log.write(f'{datetime.now()}: {self.name} (FAILED)\n')
                time.sleep(10)