From 5ac1e8efba13a7557648caea4f4034c43a9e922d Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 30 Oct 2020 21:08:55 +0100 Subject: [PATCH 01/20] adding manually migrate and change model of device --- .../68a5c025ab8e_adding_owner_id_in_device.py | 40 +++++++++++++++++++ ereuse_devicehub/resources/device/models.py | 7 ++++ 2 files changed, 47 insertions(+) create mode 100644 ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py diff --git a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py new file mode 100644 index 00000000..2cac1941 --- /dev/null +++ b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py @@ -0,0 +1,40 @@ +"""adding owner_id in device + +Revision ID: 68a5c025ab8e +Revises: b9b0ee7d9dca +Create Date: 2020-10-30 11:48:34.992498 + +""" +import sqlalchemy as sa +from alembic import context +from alembic import op +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = '68a5c025ab8e' +down_revision = 'b9b0ee7d9dca' +branch_labels = None +depends_on = None + + +def get_inv(): + # import pdb; pdb.set_trace() + INV = context.get_x_argument(as_dictionary=True).get('inventory') + INV = 'dbtest' + if not INV: + raise ValueError("Inventory value is not specified") + return INV + +def upgrade(): + op.add_column('device', sa.Column('owner_id', postgresql.UUID(), nullable=True), schema=f'{get_inv()}') + op.create_foreign_key("fk_device_owner_id_user_id", + "device", "user", + ["owner_id"], ["id"], + ondelete="SET NULL", + source_schema=f'{get_inv()}', referent_schema='common') + + +def downgrade(): + op.drop_constraint("fk_device_owner_id_user_id", "device", type_="foreignkey", schema=f'{get_inv()}') + op.drop_column('device', 'owner_id', schema=f'{get_inv()}') diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index 682327bb..e478f215 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -32,6 +32,7 @@ from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing, listener_reset from ereuse_devicehub.resources.user.models import User + class Device(Thing): """Base class for any type of physical object that can be identified. @@ -106,6 +107,12 @@ class Device(Thing): image = db.Column(db.URL) image.comment = "An image of the device." + owner_id = db.Column(UUID(as_uuid=True), + db.ForeignKey(User.id), + nullable=False, + default=lambda: g.user.id) + owner = db.relationship(User, primaryjoin=owner_id == User.id) + _NON_PHYSICAL_PROPS = { 'id', 'type', From ed8522c96a9ec352ea2c9ea6d84c718c692921bc Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 30 Oct 2020 21:39:03 +0100 Subject: [PATCH 02/20] script for set the owners to components from parent device --- migration_owners.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 migration_owners.py diff --git a/migration_owners.py b/migration_owners.py new file mode 100644 index 00000000..d1b428dc --- /dev/null +++ b/migration_owners.py @@ -0,0 +1,11 @@ +from ereuse_devicehub.resources.device import models as m +from ereuse_devicehub.db import db + +def migrate(app): + with app.app_context(): + for c in m.Component.query.filter(): + if c.parent_id: + c.owner_id = c.parent.owner.id + db.session.add(c) + db.session.commit() + db.session.flush() From f043d66591a7c7cf8e7c7cffd9ecaa0742941792 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 3 Nov 2020 19:34:58 +0100 Subject: [PATCH 03/20] migration file for add owner into device tables --- .../versions/68a5c025ab8e_adding_owner_id_in_device.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py index 2cac1941..4570cbaa 100644 --- a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py +++ b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py @@ -19,9 +19,7 @@ depends_on = None def get_inv(): - # import pdb; pdb.set_trace() INV = context.get_x_argument(as_dictionary=True).get('inventory') - INV = 'dbtest' if not INV: raise ValueError("Inventory value is not specified") return INV From 50a7b59957f78eea7586c2c184712dda80adce2b Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 3 Nov 2020 20:26:03 +0100 Subject: [PATCH 04/20] fixed bug: owner_id is not a physical property --- ereuse_devicehub/resources/device/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index e478f215..ac4cddf4 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -119,6 +119,7 @@ class Device(Thing): 'created', 'updated', 'parent_id', + 'owner_id', 'hid', 'production_date', 'color', # these are only user-input thus volatile From abe7783b93c9863bf2268eaef0249833755aa52e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Tue, 3 Nov 2020 20:26:32 +0100 Subject: [PATCH 05/20] fixed tests_devices --- tests/test_device.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/test_device.py b/tests/test_device.py index 60389d8f..b57e230c 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -128,7 +128,6 @@ def test_physical_properties(): 'ethereum_address': None, 'manufacturer': 'bar', 'model': 'foo', - 'owner_id': pc.owner_id, 'receiver_id': None, 'serial_number': 'foo-bar', 'transfer_state': TransferState.Initial @@ -401,8 +400,9 @@ def test_get_device(app: Devicehub, user: UserClient): chassis=ComputerChassis.Tower, owner_id=user.user['id']) pc.components = OrderedSet([ - d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'), - d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) + d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s', + owner_id=user.user['id']), + d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500, owner_id=user.user['id']) ]) db.session.add(pc) # todo test is an abstract class. replace with another one @@ -438,8 +438,10 @@ def test_get_devices(app: Devicehub, user: UserClient): chassis=ComputerChassis.Tower, owner_id=user.user['id']) pc.components = OrderedSet([ - d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s'), - d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500) + d.NetworkAdapter(model='c1mo', manufacturer='c1ma', serial_number='c1s', + owner_id=user.user['id']), + d.GraphicCard(model='c2mo', manufacturer='c2ma', memory=1500, + owner_id=user.user['id']) ]) pc1 = d.Desktop(model='p2mo', manufacturer='p2ma', @@ -551,22 +553,22 @@ def test_device_public(user: UserClient, client: Client): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_computer_accessory_model(): - sai = d.SAI() +def test_computer_accessory_model(user: UserClient): + sai = d.SAI(owner_id=user.user['id']) db.session.add(sai) - keyboard = d.Keyboard(layout=Layouts.ES) + keyboard = d.Keyboard(layout=Layouts.ES, owner_id=user.user['id']) db.session.add(keyboard) - mouse = d.Mouse() + mouse = d.Mouse(owner_id=user.user['id']) db.session.add(mouse) db.session.commit() @pytest.mark.mvp @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_networking_model(): - router = d.Router(speed=1000, wireless=True) +def test_networking_model(user: UserClient): + router = d.Router(speed=1000, wireless=True, owner_id=user.user['id']) db.session.add(router) - switch = d.Switch(speed=1000, wireless=False) + switch = d.Switch(speed=1000, wireless=False, owner_id=user.user['id']) db.session.add(switch) db.session.commit() From 9b25132c29b40021916b18662adf59e8286969c5 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 4 Nov 2020 17:30:12 +0100 Subject: [PATCH 06/20] fixed bug for use filters from the website --- ereuse_devicehub/resources/device/views.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index 8f16b12d..2638bafb 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -146,7 +146,7 @@ class DeviceView(View): ) def query(self, args): - query = Device.query.distinct() # todo we should not force to do this if the query is ok + query = Device.query.filter((Device.owner_id == g.user.id)).distinct() search_p = args.get('search', None) if search_p: properties = DeviceSearch.properties @@ -156,17 +156,8 @@ class DeviceView(View): ).order_by( search.Search.rank(properties, search_p) + search.Search.rank(tags, search_p) ) - query = self.visibility_filter(query) return query.filter(*args['filter']).order_by(*args['sort']) - def visibility_filter(self, query): - filterqs = request.args.get('filter', None) - if (filterqs and - 'lot' not in filterqs): - query = query.filter((Computer.id == Device.id), (Computer.owner_id == g.user.id)) - pass - return query - class DeviceMergeView(View): """View for merging two devices From ed93fa7b2920f6b7fe29025022d32d93ff373b01 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 4 Nov 2020 17:30:46 +0100 Subject: [PATCH 07/20] update the datas in the migration process --- .../68a5c025ab8e_adding_owner_id_in_device.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py index 4570cbaa..c2cad2df 100644 --- a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py +++ b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py @@ -10,7 +10,6 @@ from alembic import context from alembic import op from sqlalchemy.dialects import postgresql - # revision identifiers, used by Alembic. revision = '68a5c025ab8e' down_revision = 'b9b0ee7d9dca' @@ -25,12 +24,30 @@ def get_inv(): return INV def upgrade(): - op.add_column('device', sa.Column('owner_id', postgresql.UUID(), nullable=True), schema=f'{get_inv()}') + # We need get the actual computers with owner_id + # because when add a column in device this reset the values of the owner_id + # in the computer tables + con = op.get_bind() + # computers = con.execute(f"select id, owner_id from {get_inv()}.computer") + + op.add_column('device', sa.Column('owner_id', postgresql.UUID(), + sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), + nullable=True), schema=f'{get_inv()}') op.create_foreign_key("fk_device_owner_id_user_id", "device", "user", ["owner_id"], ["id"], ondelete="SET NULL", source_schema=f'{get_inv()}', referent_schema='common') + sql = f"select {get_inv()}.component.id, {get_inv()}.computer.owner_id from \ + {get_inv()}.component \ + inner join {get_inv()}.computer on \ + {get_inv()}.component.parent_id={get_inv()}.computer.id" + + components = con.execute(sql) + for id_component, id_owner in components: + _sql = f"update {get_inv()}.component set owner_id={id_owner} where id={id_component}" + con.execute(_sql) + def downgrade(): From 8145e606dbdefec18cbf0cfdb937087395ddbadc Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 4 Nov 2020 17:31:14 +0100 Subject: [PATCH 08/20] migration script --- migration_owners.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration_owners.py b/migration_owners.py index d1b428dc..5be44192 100644 --- a/migration_owners.py +++ b/migration_owners.py @@ -1,6 +1,8 @@ -from ereuse_devicehub.resources.device import models as m from ereuse_devicehub.db import db +from ereuse_devicehub.resources.device import models as m +[x.owner_id for x in m.Device.query.filter((m.Computer.id == m.Device.id)).all()] + def migrate(app): with app.app_context(): for c in m.Component.query.filter(): From dac0d1d32fdcc8e9f3b2f2f9864e1cf9061f1db4 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 4 Nov 2020 20:22:31 +0100 Subject: [PATCH 09/20] adding migration of datas in migrate file --- .../68a5c025ab8e_adding_owner_id_in_device.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py index c2cad2df..98a343a4 100644 --- a/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py +++ b/ereuse_devicehub/migrations/versions/68a5c025ab8e_adding_owner_id_in_device.py @@ -23,31 +23,43 @@ def get_inv(): raise ValueError("Inventory value is not specified") return INV + +def upgrade_data(): + con = op.get_bind() + computers = con.execute(f"select id, owner_id from {get_inv()}.computer") + for c in computers: + id_dev = c.id + id_owner = c.owner_id + sql = f"update {get_inv()}.device set owner_id='{id_owner}' where id={id_dev};" + con.execute(sql) + + values = f"{get_inv()}.component.id, {get_inv()}.computer.owner_id" + table = f"{get_inv()}.component" + joins = f"inner join {get_inv()}.computer" + on = f"on {table}.parent_id={get_inv()}.computer.id" + sql = f"select {values} from {table} {joins} {on}" + + components = con.execute(sql) + for c in components: + id = c.id + id_owner = c.owner_id + sql = f"update {get_inv()}.device set owner_id='{id_owner}' where id={id};" + con.execute(sql) + + def upgrade(): # We need get the actual computers with owner_id # because when add a column in device this reset the values of the owner_id # in the computer tables - con = op.get_bind() - # computers = con.execute(f"select id, owner_id from {get_inv()}.computer") - op.add_column('device', sa.Column('owner_id', postgresql.UUID(), - sa.ForeignKeyConstraint(['owner_id'], ['common.user.id'], ), nullable=True), schema=f'{get_inv()}') op.create_foreign_key("fk_device_owner_id_user_id", "device", "user", ["owner_id"], ["id"], ondelete="SET NULL", source_schema=f'{get_inv()}', referent_schema='common') - sql = f"select {get_inv()}.component.id, {get_inv()}.computer.owner_id from \ - {get_inv()}.component \ - inner join {get_inv()}.computer on \ - {get_inv()}.component.parent_id={get_inv()}.computer.id" - - components = con.execute(sql) - for id_component, id_owner in components: - _sql = f"update {get_inv()}.component set owner_id={id_owner} where id={id_component}" - con.execute(_sql) + upgrade_data() def downgrade(): From 81da7cb9a8ba1af207ab66f391600c6908e4a094 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 4 Nov 2020 20:23:12 +0100 Subject: [PATCH 10/20] rm migration_owners.py --- migration_owners.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 migration_owners.py diff --git a/migration_owners.py b/migration_owners.py deleted file mode 100644 index 5be44192..00000000 --- a/migration_owners.py +++ /dev/null @@ -1,13 +0,0 @@ -from ereuse_devicehub.db import db - -from ereuse_devicehub.resources.device import models as m -[x.owner_id for x in m.Device.query.filter((m.Computer.id == m.Device.id)).all()] - -def migrate(app): - with app.app_context(): - for c in m.Component.query.filter(): - if c.parent_id: - c.owner_id = c.parent.owner.id - db.session.add(c) - db.session.commit() - db.session.flush() From af9fe2d6b69ad310fd992e21f8b5bc3ecb7281d6 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 5 Nov 2020 16:31:57 +0100 Subject: [PATCH 11/20] fixing tests --- tests/test_agent.py | 6 +++--- tests/test_device.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_agent.py b/tests/test_agent.py index 2a3a360b..9d5f379e 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -3,7 +3,7 @@ from uuid import UUID import pytest from marshmallow import ValidationError from sqlalchemy_utils import PhoneNumber -from teal.db import UniqueViolation +from teal.db import UniqueViolation, DBError from teal.enums import Country from ereuse_devicehub.config import DevicehubConfig @@ -80,7 +80,7 @@ def test_membership_repeated(): db.session.add(person) person.member_of.add(Membership(org, person)) - with pytest.raises(UniqueViolation): + with pytest.raises(DBError): db.session.flush() @@ -95,7 +95,7 @@ def test_membership_repeating_id(): person2 = Person(name='Tommy') person2.member_of.add(Membership(org, person2, id='acme-1')) db.session.add(person2) - with pytest.raises(UniqueViolation) as e: + with pytest.raises(DBError) as e: db.session.flush() assert 'One member id per organization' in str(e) diff --git a/tests/test_device.py b/tests/test_device.py index b57e230c..9f3afcb8 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -574,8 +574,9 @@ def test_networking_model(user: UserClient): @pytest.mark.usefixtures(conftest.app_context.__name__) -def test_cooking_mixer(): - mixer = d.Mixer(serial_number='foo', model='bar', manufacturer='foobar') +def test_cooking_mixer(user: UserClient): + mixer = d.Mixer(serial_number='foo', model='bar', manufacturer='foobar', + owner_id=user.user['id']) db.session.add(mixer) db.session.commit() From 52bd6d5271db0176ad948a66f7fdaaed411f3415 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 17:09:39 +0100 Subject: [PATCH 12/20] fixed similar_one for get only from your devices --- ereuse_devicehub/resources/device/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ereuse_devicehub/resources/device/models.py b/ereuse_devicehub/resources/device/models.py index ac4cddf4..8fb9acdf 100644 --- a/ereuse_devicehub/resources/device/models.py +++ b/ereuse_devicehub/resources/device/models.py @@ -577,7 +577,8 @@ class Component(Device): """ assert self.hid is None, 'Don\'t use this method with a component that has HID' component = self.__class__.query \ - .filter_by(parent=parent, hid=None, **self.physical_properties) \ + .filter_by(parent=parent, hid=None, owner_id=self.owner_id, + **self.physical_properties) \ .filter(~Component.id.in_(blacklist)) \ .first() if not component: From 8a471c98c0d259d0ea31aaba82795f5e007eb41e Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 17:10:32 +0100 Subject: [PATCH 13/20] todo an automatic merge with sync only of your devices --- ereuse_devicehub/resources/device/sync.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/resources/device/sync.py b/ereuse_devicehub/resources/device/sync.py index 5f13d5a0..dce292ce 100644 --- a/ereuse_devicehub/resources/device/sync.py +++ b/ereuse_devicehub/resources/device/sync.py @@ -4,6 +4,7 @@ from itertools import groupby from typing import Iterable, Set import yaml +from flask import g from sqlalchemy import inspect from sqlalchemy.exc import IntegrityError from sqlalchemy.util import OrderedSet @@ -101,7 +102,7 @@ class Sync: assert inspect(component).transient, 'Component should not be synced from DB' try: if component.hid: - db_component = Device.query.filter_by(hid=component.hid).one() + db_component = Device.query.filter_by(hid=component.hid, owner_id=g.user.id).one() assert isinstance(db_component, Device), \ '{} must be a component'.format(db_component) else: @@ -153,7 +154,7 @@ class Sync: db_device = None if device.hid: with suppress(ResourceNotFound): - db_device = Device.query.filter_by(hid=device.hid).one() + db_device = Device.query.filter_by(hid=device.hid, owner_id=g.user.id).one() try: tags = {Tag.from_an_id(tag.id).one() for tag in device.tags} # type: Set[Tag] except ResourceNotFound: @@ -202,6 +203,9 @@ class Sync: This method mutates db_device. """ + if db_device.owner_id != g.user.id: + return + for field_name, value in device.physical_properties.items(): if value is not None: setattr(db_device, field_name, value) @@ -232,8 +236,11 @@ class Sync: return component.parent or Device(id=0) # Computer with id 0 is our Identity for parent, _components in groupby(sorted(adding, key=g_parent), key=g_parent): - if parent.id != 0: # Is not Computer Identity - actions.add(Remove(device=parent, components=OrderedSet(_components))) + set_components = OrderedSet(_components) + check_owners = (x.owner_id == g.user.id for x in set_components) + # Is not Computer Identity and all components have the correct owner + if parent.id != 0 and all(check_owners): + actions.add(Remove(device=parent, components=set_components)) return actions From 3eecdb4a1e42b492b97e49b64f87e472a4ca9067 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 17:13:37 +0100 Subject: [PATCH 14/20] filter from owners in get private device or redirect to public and filter devices from owner the manual merge --- ereuse_devicehub/resources/device/views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ereuse_devicehub/resources/device/views.py b/ereuse_devicehub/resources/device/views.py index 2638bafb..ad2702f0 100644 --- a/ereuse_devicehub/resources/device/views.py +++ b/ereuse_devicehub/resources/device/views.py @@ -98,7 +98,7 @@ class DeviceView(View): return super().get(id) def patch(self, id): - dev = Device.query.filter_by(id=id).one() + dev = Device.query.filter_by(id=id, owner_id=g.user.id).one() if isinstance(dev, Computer): resource_def = app.resources['Computer'] # TODO check how to handle the 'actions_one' @@ -128,9 +128,9 @@ class DeviceView(View): @auth.Auth.requires_auth def one_private(self, id: int): - device = Device.query.filter_by(id=id).one() - if hasattr(device, 'owner_id') and device.owner_id != g.user.id: - device = {} + device = Device.query.filter_by(id=id, owner_id=g.user.id).first() + if not device: + return self.one_public(id) return self.schema.jsonify(device) @auth.Auth.requires_auth @@ -172,8 +172,8 @@ class DeviceMergeView(View): return args['id'] def post(self, id: uuid.UUID): - device = Device.query.filter_by(id=id).one() - with_device = Device.query.filter_by(id=self.get_merge_id()).one() + device = Device.query.filter_by(id=id, owner_id=g.user.id).one() + with_device = Device.query.filter_by(id=self.get_merge_id(), owner_id=g.user.id).one() self.merge_devices(device, with_device) db.session().final_flush() From bc923d55769cd6c747ca1dc9e9f40a6645dbccff Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 17:14:16 +0100 Subject: [PATCH 15/20] fixed test for check with owners --- tests/test_device.py | 27 ++++++++++++++++++--------- tests/test_documents.py | 24 ++++++++++++++++++++++++ tests/test_snapshot.py | 4 +++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/tests/test_device.py b/tests/test_device.py index 9f3afcb8..d288b964 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -137,6 +137,7 @@ def test_physical_properties(): @pytest.mark.mvp @pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_component_similar_one(): + user = User.query.filter().first() snapshot = conftest.file('pc-components.db') pc = snapshot['device'] snapshot['components'][0]['serial_number'] = snapshot['components'][1]['serial_number'] = None @@ -145,7 +146,8 @@ def test_component_similar_one(): db.session.add(pc) db.session.flush() # Let's create a new component named 'A' similar to 1 - componentA = d.Component(model=component1.model, manufacturer=component1.manufacturer) + componentA = d.Component(model=component1.model, manufacturer=component1.manufacturer, + owner_id=user.id) similar_to_a = componentA.similar_one(pc, set()) assert similar_to_a == component1 # d.Component B does not have the same model @@ -164,16 +166,17 @@ def test_add_remove(): # pc has c1 and c2 # pc2 has c3 # c4 is not with any pc + user = User.query.filter().first() values = conftest.file('pc-components.db') pc = values['device'] c1, c2 = (d.Component(**c) for c in values['components']) pc = d.Desktop(**pc, components=OrderedSet([c1, c2])) db.session.add(pc) - c3 = d.Component(serial_number='nc1') + c3 = d.Component(serial_number='nc1', owner_id=user.id) pc2 = d.Desktop(serial_number='s2', components=OrderedSet([c3]), chassis=ComputerChassis.Microtower) - c4 = d.Component(serial_number='c4s') + c4 = d.Component(serial_number='c4s', owner_id=user.id) db.session.add(pc2) db.session.add(c4) db.session.commit() @@ -312,14 +315,16 @@ def test_sync_execute_register_no_hid_tag_not_linked(tag_id: str): @pytest.mark.mvp -@pytest.mark.usefixtures(conftest.app_context.__name__) +@pytest.mark.usefixtures(conftest.auth_app_context.__name__) def test_sync_execute_register_tag_does_not_exist(): """Ensures not being able to register if the tag does not exist, even if the device has HID or it existed before. Tags have to be created before trying to link them through a Snapshot. """ + user = User.query.filter().first() pc = d.Desktop(**conftest.file('pc-components.db')['device'], tags=OrderedSet([Tag('foo')])) + pc.owner_id = user.id with raises(ResourceNotFound): Sync().execute_register(pc) @@ -463,17 +468,21 @@ def test_get_devices(app: Devicehub, user: UserClient): @pytest.mark.mvp -def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient): +def test_get_device_permissions(app: Devicehub, user: UserClient, user2: UserClient, + client: Client): """Checks GETting a d.Desktop with its components.""" - user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) - pc, res = user.get("/devices/1", None) + s, _ = user.post(file('asus-eee-1000h.snapshot.11'), res=m.Snapshot) + pc, res = user.get(res=d.Device, item=s['device']['id']) assert res.status_code == 200 assert len(pc['actions']) == 9 - pc2, res2 = user2.get("/devices/1", None) + html, _ = client.get(res=d.Device, item=s['device']['id'], accept=ANY) + assert 'intel atom cpu n270 @ 1.60ghz' in html + assert '00:24:8C:7F:CF:2D – 100 Mbps' in html + pc2, res2 = user2.get(res=d.Device, item=s['device']['id'], accept=ANY) assert res2.status_code == 200 - assert pc2 == {} + assert pc2 == html @pytest.mark.mvp diff --git a/tests/test_documents.py b/tests/test_documents.py index 0736856d..281ca65a 100644 --- a/tests/test_documents.py +++ b/tests/test_documents.py @@ -4,6 +4,7 @@ from io import StringIO from pathlib import Path import pytest +from werkzeug.exceptions import Unauthorized import teal.marshmallow from ereuse_utils.test import ANY @@ -79,6 +80,29 @@ def test_erasure_certificate_wrong_id(client: Client): status=teal.marshmallow.ValidationError) +@pytest.mark.mvp +def test_export_csv_permitions(user: UserClient, user2: UserClient, client: Client): + """Test export device information in a csv file with others users.""" + snapshot, _ = user.post(file('basic.snapshot'), res=Snapshot) + csv_user, _ = user.get(res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})]) + + csv_user2, _ = user2.get(res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})]) + + _, res = client.get(res=documents.DocumentDef.t, + item='devices/', + accept='text/csv', + query=[('filter', {'type': ['Computer']})], status=401) + assert res.status_code == 401 + + assert len(csv_user) > 0 + assert len(csv_user2) == 0 + @pytest.mark.mvp def test_export_basic_snapshot(user: UserClient): """Test export device information in a csv file.""" diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index 28badb66..8319d85b 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -252,7 +252,9 @@ def test_snapshot_component_add_remove(user: UserClient): assert {c['serialNumber'] for c in pc1['components']} == {'p1c3s', 'p1c4s'} assert all(c['parent'] == pc1_id for c in pc1['components']) # This last Action only - assert get_actions_info(pc1['actions'])[-1] == ('RateComputer', ['p1c3s', 'p1c4s']) + act = get_actions_info(pc1['actions'])[-1] + assert 'RateComputer' in act + assert set(act[1]) == {'p1c3s', 'p1c4s'} # PC2 # We haven't changed PC2 assert tuple(c['serialNumber'] for c in pc2['components']) == ('p2c1s',) From bc5cbb2effdede1949fb58f936e4752d5b3c4dfe Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 20:48:09 +0100 Subject: [PATCH 16/20] same computer saved from diferents users --- tests/test_snapshot.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index 8319d85b..5398871b 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -1,6 +1,7 @@ import os import json import pytest +import uuid from datetime import datetime, timedelta, timezone from requests.exceptions import HTTPError @@ -103,6 +104,24 @@ def test_snapshot_post(user: UserClient): assert rate['snapshot']['id'] == snapshot['id'] +@pytest.mark.mvp +def test_same_device_tow_users(user: UserClient, user2: UserClient): + """Two users can up the same snapshot and the system save 2 computers""" + user.post(file('basic.snapshot'), res=Snapshot) + i, _ = user.get(res=m.Device) + pc = next(d for d in i['items'] if d['type'] == 'Desktop') + pc_id = pc['id'] + assert i['items'][0]['url'] == f'/devices/{pc_id}' + + basic_snapshot = file('basic.snapshot') + basic_snapshot['uuid'] = f"{uuid.uuid4()}" + user2.post(basic_snapshot, res=Snapshot) + i2, _ = user2.get(res=m.Device) + pc2 = next(d for d in i2['items'] if d['type'] == 'Desktop') + assert pc['id'] != pc2['id'] + assert pc['ownerID'] != pc2['ownerID'] + assert pc['hid'] == pc2['hid'] + @pytest.mark.mvp def test_snapshot_update_timefield_updated(user: UserClient): """ From 2055af222dc496395245d635602a38d9de8155c1 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Fri, 6 Nov 2020 21:01:29 +0100 Subject: [PATCH 17/20] checking if 2 users find the same computer but registered 2 times --- tests/test_device_find.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_device_find.py b/tests/test_device_find.py index b80896e1..7e9ac291 100644 --- a/tests/test_device_find.py +++ b/tests/test_device_find.py @@ -1,4 +1,5 @@ import pytest +import uuid from teal.utils import compiled from ereuse_devicehub.client import UserClient @@ -185,6 +186,26 @@ def test_device_query(user: UserClient): assert not pc['tags'] +@pytest.mark.mvp +def test_device_query_permitions(user: UserClient, user2: UserClient): + """Checks result of inventory for two users""" + user.post(file('basic.snapshot'), res=Snapshot) + i, _ = user.get(res=Device) + pc1 = next(d for d in i['items'] if d['type'] == 'Desktop') + + i2, _ = user2.get(res=Device) + assert i2['items'] == [] + + basic_snapshot = file('basic.snapshot') + basic_snapshot['uuid'] = f"{uuid.uuid4()}" + user2.post(basic_snapshot, res=Snapshot) + i2, _ = user2.get(res=Device) + pc2 = next(d for d in i2['items'] if d['type'] == 'Desktop') + + assert pc1['id'] != pc2['id'] + assert pc1['hid'] == pc2['hid'] + + @pytest.mark.mvp def test_device_search_all_devices_token_if_empty(app: Devicehub, user: UserClient): """Ensures DeviceSearch can regenerate itself when the table is empty.""" From 634806ec1ce74b6e4d14a1655b8c302cddcdd551 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 9 Dec 2020 12:37:20 +0100 Subject: [PATCH 18/20] fixing security query in live action --- ereuse_devicehub/resources/action/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index 06ae8320..aecdd968 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -237,12 +237,12 @@ class ActionView(View): def live(self, snapshot): """If the device.allocated == True, then this snapshot create an action live.""" device = snapshot.get('device') # type: Computer - # TODO @cayop dependency of pulls 85 and 83 - # if the pr/85 and pr/83 is merged, then you need change this way for get the device - if not device.hid or not Device.query.filter(Device.hid==device.hid).count(): + # TODO @cayop dependency of pulls 85 + # if the pr/85 is merged, then you need change this way for get the device + if not device.hid or not Device.query.filter(Device.hid==device.hid, owner_id=g.user.id).count(): return None - device = Device.query.filter(Device.hid==device.hid).one() + device = Device.query.filter(Device.hid==device.hid, owner_id=g.user.id).one() if not device.allocated: return None From 16132414e65052cea331a69a6800dd516053c3bd Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Wed, 9 Dec 2020 12:49:56 +0100 Subject: [PATCH 19/20] fixing bug in query --- ereuse_devicehub/resources/action/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ereuse_devicehub/resources/action/views.py b/ereuse_devicehub/resources/action/views.py index aecdd968..7cd4f53a 100644 --- a/ereuse_devicehub/resources/action/views.py +++ b/ereuse_devicehub/resources/action/views.py @@ -239,10 +239,12 @@ class ActionView(View): device = snapshot.get('device') # type: Computer # TODO @cayop dependency of pulls 85 # if the pr/85 is merged, then you need change this way for get the device - if not device.hid or not Device.query.filter(Device.hid==device.hid, owner_id=g.user.id).count(): + if not device.hid or not Device.query.filter( + Device.hid==device.hid, Device.owner_id==g.user.id).count(): return None - device = Device.query.filter(Device.hid==device.hid, owner_id=g.user.id).one() + device = Device.query.filter( + Device.hid==device.hid, Device.owner_id==g.user.id).one() if not device.allocated: return None From 5d6a84a18fc6181e88e1bcdbf96d2fb39ce7d140 Mon Sep 17 00:00:00 2001 From: Cayo Puigdefabregas Date: Thu, 10 Dec 2020 10:22:51 +0100 Subject: [PATCH 20/20] adding CHANGELOG file --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1ba9a808 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.ht +ml). + +## master + [1.0.1-beta] + +## testing + [1.0.2-beta] + +## [1.0.2-beta] +- [addend] #87 allocate, deallocate and live actions +- [fixed] #89 save json on disk only for shapshots +- [addend] #83 add owner_id in all kind of device