Exemple #1
0
    def validate(self, data):
        """All devices need to have the status of revoke."""

        if not data['action'].type == 'RevokeDocument':
            txt = 'Error: this action is not a revoke action'
            ValidationError(txt)

        for doc in data['documents']:
            if not doc.trading == 'Revoke':
                txt = 'Some of documents do not have revoke to confirm'
                ValidationError(txt)
Exemple #2
0
    def validate(self, data):
        """All devices need to have the status of DoubleConfirmation."""

        ### check ###
        if not data['documents']:
            raise ValidationError('Documents not exist.')

        for doc in data['documents']:
            if not doc.trading in ['Document Confirmed', 'Confirm']:
                txt = 'Some of documents do not have enough to confirm for to do a revoke'
                ValidationError(txt)
Exemple #3
0
    def merge_devices(self, dev1_id: int, dev2_id: int) -> Device:
        """Merge the current device with `with_device` (dev2_id) by
        adding all `with_device` actions under the current device, (dev1_id).

        This operation is highly costly as it forces refreshing
        many models in session.
        """
        # base_device = Device.query.filter_by(id=dev1_id, owner_id=g.user.id).one()
        self.base_device = Device.query.filter_by(id=dev1_id, owner_id=g.user.id).one()
        self.with_device = Device.query.filter_by(id=dev2_id, owner_id=g.user.id).one()

        if self.base_device.allocated or self.with_device.allocated:
            # Validation than any device is allocated
            msg = 'The device is allocated, please deallocated before merge.'
            raise ValidationError(msg)

        if not self.base_device.type == self.with_device.type:
            # Validation than we are speaking of the same kind of devices
            raise ValidationError('The devices is not the same type.')

        # Adding actions of self.with_device
        with_actions_one = [a for a in self.with_device.actions
                            if isinstance(a, actions.ActionWithOneDevice)]
        with_actions_multiple = [a for a in self.with_device.actions
                                 if isinstance(a, actions.ActionWithMultipleDevices)]

        # Moving the tags from `with_device` to `base_device`
        # Union of tags the device had plus the (potentially) new ones
        self.base_device.tags.update([x for x in self.with_device.tags])
        self.with_device.tags.clear()  # We don't want to add the transient dummy tags
        db.session.add(self.with_device)

        # Moving the actions from `with_device` to `base_device`
        for action in with_actions_one:
            if action.parent:
                action.parent = self.base_device
            else:
                self.base_device.actions_one.add(action)
        for action in with_actions_multiple:
            if action.parent:
                action.parent = self.base_device
            else:
                self.base_device.actions_multiple.add(action)

        # Keeping the components of with_device
        components = OrderedSet(c for c in self.with_device.components)
        self.base_device.components = components

        # Properties from with_device
        self.merge()

        db.session().add(self.base_device)
        db.session().final_flush()
        return self.base_device
Exemple #4
0
    def live(self, snapshot):
        """If the device.allocated == True, then this snapshot create an action live."""
        hid = self.get_hid(snapshot)
        if not hid or not Device.query.filter(Device.hid == hid).count():
            raise ValidationError('Device not exist.')

        device = Device.query.filter(Device.hid == hid,
                                     Device.allocated == True).one()
        # Is not necessary
        if not device:
            raise ValidationError('Device not exist.')
        if not device.allocated:
            raise ValidationError('Sorry this device is not allocated.')

        usage_time_hdd, serial_number = self.get_hdd_details(snapshot, device)

        data_live = {
            'usage_time_hdd': usage_time_hdd,
            'serial_number': serial_number,
            'snapshot_uuid': snapshot['uuid'],
            'description': '',
            'software': snapshot['software'],
            'software_version': snapshot['version'],
            'licence_version': snapshot['licence_version'],
            'author_id': device.owner_id,
            'agent_id': device.owner.individual.id,
            'device': device
        }

        live = Live(**data_live)

        if not usage_time_hdd:
            warning = f"We don't found any TestDataStorage for disk sn: {serial_number}"
            live.severity = Severity.Warning
            live.description = warning
            return live

        live.sort_actions()
        diff_time = live.diff_time()
        if diff_time is None:
            warning = "Don't exist one previous live or snapshot as reference"
            live.description += warning
            live.severity = Severity.Warning
        elif diff_time < timedelta(0):
            warning = "The difference with the last live/snapshot is negative"
            live.description += warning
            live.severity = Severity.Warning
        return live
Exemple #5
0
    def create_confirmations(self) -> None:
        """Do the first confirmation for the user than do the action"""

        # if the confirmation is mandatory, do automatic confirmation only for
        # owner of the lot
        if self.trade.confirm:
            if self.trade.devices:
                confirm_devs = Confirm(user=g.user,
                                       action=self.trade,
                                       devices=self.trade.devices)
                db.session.add(confirm_devs)

            if self.trade.documents:
                confirm_docs = ConfirmDocument(user=g.user,
                                               action=self.trade,
                                               documents=self.trade.documents)
                db.session.add(confirm_docs)
            return

        # check than the user than want to do the action is one of the users
        # involved in the action
        if g.user not in [self.trade.user_from, self.trade.user_to]:
            txt = "You do not participate in this trading"
            raise ValidationError(txt)

        confirm_from = Confirm(user=self.trade.user_from,
                               action=self.trade,
                               devices=self.trade.devices)
        confirm_to = Confirm(user=self.trade.user_to,
                             action=self.trade,
                             devices=self.trade.devices)
        db.session.add(confirm_from)
        db.session.add(confirm_to)
Exemple #6
0
 def __init__(self, data, resource_def, schema):
     self.schema = schema
     a = resource_def.schema.load(data)
     self.validate(a)
     if not a['documents']:
         raise ValidationError('Documents not exist.')
     self.model = self.Model(**a)
Exemple #7
0
 def validate_snapshot_events(self, data):
     """Validates that only snapshot-related events can be uploaded."""
     from ereuse_devicehub.resources.event.models import EraseBasic, Test, Rate, Install, \
         Benchmark
     for event in data['events_one']:
         if not isinstance(event,
                           (Install, EraseBasic, Rate, Test, Benchmark)):
             raise ValidationError('You cannot upload {}'.format(event),
                                   field_names=['events'])
Exemple #8
0
def decode_snapshot(data):
    try:
        return jwt.decode(data['data'],
                          app.config['JWT_PASS'],
                          algorithms="HS256",
                          json_encoder=ereuse_utils.JSONEncoder)
    except jwt.exceptions.InvalidSignatureError as err:
        txt = 'Invalid snapshot'
        raise ValidationError(txt)
Exemple #9
0
 def validate(self, data):
     """If there are one device than have one confirmation,
        then remove the list this device of the list of devices of this action
     """
     for doc in data['documents']:
         ac = doc.trading
         if not doc.trading in ['Confirm', 'Need Confirmation']:
             txt = 'Some of documents do not have enough to confirm for to do a Doble Confirmation'
             ValidationError(txt)
Exemple #10
0
    def validate(self, data):
        """All devices need to have the status of DoubleConfirmation."""

        devices = data['devices']
        if not devices:
            raise ValidationError('Devices not exist.')

        lot = data['action'].lot
        self.model = delete_from_trade(lot, devices)
Exemple #11
0
    def run(self,
            device: Device,
            components: Iterable[Component] or None) -> (Device, OrderedSet):
        """Synchronizes the device and components with the database.

        Identifies if the device and components exist in the database
        and updates / inserts them as necessary.

        Passed-in parameters have to be transient, or said differently,
        not-db-synced objects, or otherwise they would end-up being
        added in the session. `Learn more... <http://docs.sqlalchemy.org/
        en/latest/orm/session_state_management.html#quickie-intro-to
        -object-states>`_.

        This performs Add / Remove as necessary.

        :param device: The device to add / update to the database.
        :param components: Components that are inside of the device.
                           This method performs Add and Remove actions
                           so the device ends up with these components.
                           Components are added / updated accordingly.
                           If this is empty, all components are removed.
                           If this is None, it means that we are not
                           providing info about the components, in which
                           case we keep the already existing components
                           of the device –we don't touch them.
        :return: A tuple of:

                 1. The device from the database (with an ID) whose
                    ``components`` field contain the db version
                    of the passed-in components.
                 2. A list of Add / Remove (not yet added to session).
        """
        db_device = self.execute_register(device)
        db_components, actions = OrderedSet(), OrderedSet()
        if components is not None:  # We have component info (see above)
            if not isinstance(db_device, Computer):
                # Until a good reason is given, we synthetically forbid
                # non-computers with components
                raise ValidationError('Only computers can have components.')
            blacklist = set()  # type: Set[int]
            not_new_components = set()
            for component in components:
                db_component, is_new = self.execute_register_component(component,
                                                                       blacklist,
                                                                       parent=db_device)
                db_components.add(db_component)
                if not is_new:
                    not_new_components.add(db_component)
            # We only want to perform Add/Remove to not new components
            actions = self.add_remove(db_device, not_new_components)
            db_device.components = db_components
        return db_device, actions
Exemple #12
0
    def validate(self, data):
        """If there are one device than have one confirmation,
           then remove the list this device of the list of devices of this action
        """
        real_devices = []
        trade = data['action']
        lot = trade.lot
        for dev in data['devices']:
            if dev.trading(lot, simple=True) not in [
                    'NeedConfirmation', 'NeedConfirmRevoke'
            ]:
                raise ValidationError('Some devices not possible confirm.')

        # Change the owner for every devices
        for dev in data['devices']:
            if dev.trading(lot) == 'NeedConfirmation':
                user_to = data['action'].user_to
                dev.change_owner(user_to)
Exemple #13
0
 def post(self):
     """Posts an event."""
     json = request.get_json(validate=False)
     if not json or 'type' not in json:
         raise ValidationError('Resource needs a type.')
     # todo there should be a way to better get subclassess resource
     #   defs
     resource_def = app.resources[json['type']]
     e = resource_def.schema.load(json)
     if json['type'] == Snapshot.t:
         return self.snapshot(e, resource_def)
     Model = db.Model._decl_class_registry.data[json['type']]()
     event = Model(**e)
     db.session.add(event)
     db.session().final_flush()
     ret = self.schema.jsonify(event)
     ret.status_code = 201
     db.session.commit()
     return ret
Exemple #14
0
    def execute_register(self, device: Device) -> Device:
        """
        Synchronizes one device to the DB.

        This method tries to get an existing device using the HID
        or one of the tags, and...

        - if it already exists it returns a "local synced version"
          –the same ``device`` you passed-in but with updated values
          from the database. In this case we do not
          "touch" any of its values on the DB.
        - If it did not exist, a new device is created in the db.

        This method validates that all passed-in tags (``device.tags``),
        if linked, are linked to the same device, ditto for the hid.
        Finally it links the tags with the device.

        If you pass-in a component that is inside a parent, use
        :meth:`.execute_register_component` as it has more specialized
        methods to handle them.

        :param device: The device to synchronize to the DB.
        :raise NeedsId: The device has not any identifier we can use.
                        To still create the device use
                        ``force_creation``.
        :raise DatabaseError: Any other error from the DB.
        :return: The synced device from the db with the tags linked.
        """
        assert inspect(
            device).transient, 'Device cannot be already synced from DB'
        assert all(inspect(tag).transient
                   for tag in device.tags), 'Tags cannot be synced from DB'
        if not device.tags and not device.hid:
            # We cannot identify this device
            raise NeedsId()
        db_device = None
        if device.hid:
            with suppress(ResourceNotFound):
                db_device = Device.query.filter_by(hid=device.hid).one()
        try:
            tags = {Tag.from_an_id(tag.id).one()
                    for tag in device.tags}  # type: Set[Tag]
        except ResourceNotFound:
            raise ResourceNotFound(
                'tag you are linking to device {}'.format(device))
        linked_tags = {tag for tag in tags if tag.device_id}  # type: Set[Tag]
        if linked_tags:
            sample_tag = next(iter(linked_tags))
            for tag in linked_tags:
                if tag.device_id != sample_tag.device_id:
                    raise MismatchBetweenTags(
                        tag, sample_tag)  # Tags linked to different devices
            if db_device:  # Device from hid
                if sample_tag.device_id != db_device.id:  # Device from hid != device from tags
                    raise MismatchBetweenTagsAndHid(db_device.id,
                                                    db_device.hid)
            else:  # There was no device from hid
                if sample_tag.device.physical_properties != device.physical_properties:
                    # Incoming physical props of device != props from tag's device
                    # which means that the devices are not the same
                    raise MismatchBetweenProperties(
                        sample_tag.device.physical_properties,
                        device.physical_properties)
                db_device = sample_tag.device
        if db_device:  # Device from hid or tags
            self.merge(device, db_device)
        else:  # Device is new and tags are not linked to a device
            device.tags.clear(
            )  # We don't want to add the transient dummy tags
            db.session.add(device)
            db_device = device
        db_device.tags |= tags  # Union of tags the device had plus the (potentially) new ones
        try:
            db.session.flush()
        except IntegrityError as e:
            # Manage 'one tag per organization' unique constraint
            if 'One tag per organization' in e.args[0]:
                # todo test for this
                id = int(e.args[0][135:e.args[0].index(',', 135)])
                raise ValidationError('The device is already linked to tag {} '
                                      'from the same organization.'.format(id),
                                      field_names=['device.tags'])
            else:
                raise
        assert db_device is not None
        return db_device
Exemple #15
0
 def use_only_domain(self, _, url: URL):
     if url.path:
         raise ValidationError('Provider can only contain scheme and host',
                               field_names=['provider'])
     return url
Exemple #16
0
 def does_not_contain_slash(self, _, value: str):
     if '/' in value:
         raise ValidationError('Tags cannot contain slashes (/).')
     return value
Exemple #17
0
 def validate_meid(self, _, value: str):
     if not meid.is_valid(value):
         raise ValidationError('{} is not a valid meid.'.format(value))
     return value
Exemple #18
0
 def validate_imei(self, _, value: int):
     if not imei.is_valid(str(value)):
         raise ValidationError('{} is not a valid imei.'.format(value))
     return value
Exemple #19
0
 def does_not_contain_slash(self, _, value: str):
     if '/' in value:
         raise ValidationError('Name cannot contain slash \'')
     return value
Exemple #20
0
    def post(self):
        """Posts an action."""

        json = request.get_json(validate=False)

        if not json or 'type' not in json:
            raise ValidationError('Post request needs a json.')
        # todo there should be a way to better get subclassess resource
        #   defs
        resource_def = app.resources[json['type']]
        if json['type'] == Snapshot.t:
            if json.get('software') == 'Web' and json['device'] == 'Computer':
                txt = 'Invalid snapshot'
                raise ValidationError(txt)

            if json.get('software') == 'Web':
                snapshot = SnapshotView(json, resource_def, self.schema)
                return snapshot.post()

            # TODO @cayop uncomment at four weeks
            # if not 'data' in json:
            # txt = 'Invalid snapshot'
            # raise ValidationError(txt)

            # snapshot_data = decode_snapshot(json)

            snapshot_data = json
            if 'data' in json:
                snapshot_data = decode_snapshot(json)

            if not snapshot_data:
                txt = 'Invalid snapshot'
                raise ValidationError(txt)

            snapshot = SnapshotView(snapshot_data, resource_def, self.schema)
            return snapshot.post()

        if json['type'] == VisualTest.t:
            pass
            # TODO JN add compute rate with new visual test and old components device

        if json['type'] == InitTransfer.t:
            return self.transfer_ownership()

        if json['type'] == Trade.t:
            trade = trade_view.TradeView(json, resource_def, self.schema)
            return trade.post()

        if json['type'] == Confirm.t:
            confirm = trade_view.ConfirmView(json, resource_def, self.schema)
            return confirm.post()

        if json['type'] == Revoke.t:
            revoke = trade_view.RevokeView(json, resource_def, self.schema)
            return revoke.post()

        if json['type'] == 'ConfirmRevoke':
            revoke = trade_view.RevokeView(json, resource_def, self.schema)
            return revoke.post()

        if json['type'] == 'RevokeDocument':
            revoke = trade_view.RevokeDocumentView(json, resource_def,
                                                   self.schema)
            return revoke.post()

        if json['type'] == 'ConfirmDocument':
            confirm = trade_view.ConfirmDocumentView(json, resource_def,
                                                     self.schema)
            return confirm.post()

        if json['type'] == 'ConfirmRevokeDocument':
            confirm_revoke = trade_view.ConfirmRevokeDocumentView(
                json, resource_def, self.schema)
            return confirm_revoke.post()

        if json['type'] == 'DataWipe':
            erased = ErasedView(json, resource_def.schema)
            return erased.post()

        a = resource_def.schema.load(json)
        Model = db.Model._decl_class_registry.data[json['type']]()
        action = Model(**a)
        db.session.add(action)
        db.session().final_flush()
        ret = self.schema.jsonify(action)
        ret.status_code = 201
        db.session.commit()
        return ret