Exemple #1
0
    def test_create_block(self):
        brl = BRLBlock("goku/goku/block/master")

        # Always can make a block if its public
        ensure = Security("goku", self.store)
        self._subscribe("goku", "free")
        ensure.check_create_block(brl.owner, private=False)

        # Only can make a private block if subscription is ok
        self.assertRaises(ForbiddenException, ensure.check_create_block, brl.owner, private=True)
        self._subscribe("goku", "enterprise_275_50_x")
        ensure.check_create_block(brl.owner, private=True)

        # Other user can create a block in my namespace if its an admin, not write and read is enought
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_create_block, brl.owner, private=True)

        bper = ElementPermissions(brl.owner, private=True)
        bper.write.grant("bulma")
        bper.read.grant("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_create_block, brl.owner, private=True)

        # 3. But an admin user can delete it
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_create_block(brl.owner, private=True)
        ensure.check_create_block(brl.owner, private=False)
    def test_publish_dev_with_tag(self, enqueuer):
        brl = BRLBlock('owner/user/block/branch')
        store = Mock(MongoServerStore)
        store.read_block_permissions = Mock(return_value=ElementPermissions(brl, private=False))
        user = Mock()
        user.blocks = {}
        store.read_user = Mock(return_value=user)

        block = Mock(Block)
        block.add_publication.return_value = (['mock_id'], [], [], [])
        block.deltas = []
        ensure = Security('authUser', store)
        ensure.check_read_block = Mock(return_value=True)
        ensure.check_create_block = Mock(return_value=True)
        ensure.check_write_block = Mock(return_value=True)
        ensure.check_publish_block = Mock(return_value=True)

        store.read_block.return_value = block
        store.read_published_cells.return_value = {}
        p = PublishService(store, 'authUser')
        p.security = ensure
        pack = PublishRequest(BlockVersion(brl, -1))
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.deptable = BlockVersionTable()
        p.publish(pack)
    def test_publish_dev_with_tag(self, enqueuer):
        brl = BRLBlock('owner/user/block/branch')
        store = Mock(MongoServerStore)
        store.read_block_permissions = Mock(
            return_value=ElementPermissions(brl, private=False))
        user = Mock()
        user.blocks = {}
        store.read_user = Mock(return_value=user)

        block = Mock(Block)
        block.add_publication.return_value = (['mock_id'], [], [], [])
        block.deltas = []
        ensure = Security('authUser', store)
        ensure.check_read_block = Mock(return_value=True)
        ensure.check_create_block = Mock(return_value=True)
        ensure.check_write_block = Mock(return_value=True)
        ensure.check_publish_block = Mock(return_value=True)

        store.read_block.return_value = block
        store.read_published_cells.return_value = {}
        p = PublishService(store, 'authUser')
        p.security = ensure
        pack = PublishRequest(BlockVersion(brl, -1))
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.deptable = BlockVersionTable()
        p.publish(pack)
    def test_publish(self, enqueuer):
        brl = BRLBlock('owner/user/block/branch')
        # moduleID=BlockID(UserID(123),456)
        store = Mock(MongoServerStore)
        store.read_block_permissions = Mock(return_value=ElementPermissions(brl, private=False))
        user = User("owner")
        user.numeric_id = 1
        user.blocks = {}
        store.read_user = Mock(return_value=user)
        block = Mock(Block)
        block.last_version.return_value = Mock(BlockVersion)
        block.add_publication.return_value = (Mock(list), Mock(list), Mock(list), Mock(list))
        block.deltas = []

        ensure = Security('authUser', store)
        ensure.check_create_block = Mock(return_value=True)
        ensure.check_write_block = Mock(return_value=True)
        ensure.check_read_block = Mock(return_value=True)

        store.read_block.return_value = block
        store.read_published_cells.return_value = {}
        p = PublishService(store, 'authUser')
        p.security = ensure

        pack = PublishRequest(BlockVersion(brl, -1))
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.contents['r1.h'] = Content(id_=None, load=Blob('hola'))
        pack.cells.append(SimpleCell('user/block/r2.h'))
        pack.contents['r2.h'] = Content(id_=None, load=Blob('hola'))
        pack.cells.append(SimpleCell('user/block/r3.h'))
        pack.contents['r3.h'] = Content(id_=None, load=Blob('hola'))
        pack.deptable = BlockVersionTable()
        p.publish(pack)

        block.add_publication.assert_called_once_with(pack, p.auth_user)
        store.update_block.assert_called_once_with(block)
        self.assertEqual(1, store.create_published_cells.call_count)
        self.assertEqual(1, store.create_published_contents.call_count)

        # Check sizes
        self.assertEquals(user.blocks_bytes, 12)  # 12 bytes "hola" * 3

        # Publish again, see the size incremented
        pack._bytes = None  # Lazy computed
        p.publish(pack)
        self.assertEquals(user.blocks_bytes, 24)  # 24 bytes: "hola" * 3 * 2 publications

        # Max size exceeded for user
        user.max_workspace_size = 25
        self.assertRaises(ForbiddenException, p.publish, pack)

        # Try to publish only 1 byte
        pack._bytes = None  # Lazy computed
        pack.cells = []
        pack.contents = {}
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.contents['r1.h'] = Content(id_=None, load=Blob('h'))
        p.publish(pack)
class PublishService(object):
    ''' Service for publish blocks in server.'''
    def __init__(self, store, auth_user):
        self._store = store
        self.auth_user = auth_user
        self.security = Security(self.auth_user, self._store)

    def publish(self, publish_request):
        '''Performs a publication
        TIP: If we add publish_request to transaction_definition we can easily have asynchronous
        publications

        private: Only for first publication
        '''
        from biicode.server.background.enqueuer import register_publish

        if publish_request.tag == DEV:
            if not publish_request:
                raise BiiRequestErrorException('Up to date, nothing to publish')
            if publish_request.versiontag is not None:
                raise PublishException('A DEV version cannot have tag %s' % publish_request.tag)

        assert publish_request.deptable is not None

        # by default it is public
        # TODO: BLock creation is not handled in the transaction
        target_version = publish_request.parent
        user = self._store.read_user(target_version.block.owner)
        # Look if user has the block already created, because the block
        # can exist with -1 version if it has been created in web
        if target_version.block not in user.blocks.keys():
            try:
                if target_version != publish_request.parent:  # Branching
                    user = self.create_block(target_version.block,
                                             publish_request.parent, private=False)
                else:
                    user = self.create_block(target_version.block, private=False)
            except DuplicateBlockException:
                pass  # Its ok, already created

        target_block = target_version.block
        self._store.requestBlockTransaction(target_block)
        try:
            # If we can't read the block, we can't know about his existence
            self.security.check_read_block(target_block)
            self.security.check_publish_block(target_block, publish_request)
            # biiresponse.debug('Read block "%s"' % brl_block)
            block = self._store.read_block(target_block)
            (cells, contents,
             old_cells_ids, old_content_ids) = self._in_memory_block_update(block, publish_request)
        except ForbiddenException:
            self._store.finishBlockTransaction(target_block)
            raise
        except PublishException as e:
            self._store.finishBlockTransaction(target_block)
            raise ServerInternalErrorException(e.message)
        except Exception as excp:
            logger.error("Exception in publish service!!: %s " % str(excp))
            tb = traceback.format_exc()
            logger.error(tb)
            self._store.finishBlockTransaction(target_block)
            raise ServerInternalErrorException()

        self._store.beginBlockTransaction(target_block, cells, contents)
        try:
            self._write_resources_to_db(cells, contents, old_cells_ids, old_content_ids)
            self._store.update_block(block)
            self._store.commitBlockTransaction(target_block)
            register_publish(self.auth_user, block.last_version())
            self._store.finishBlockTransaction(target_block)

            # Need to read user again, otherwise will raise MongoNotCurrentObjectException
            # because of double update of same memory object
            user = self._store.read_user(target_version.block.owner)
            user.add_block_size_bytes(target_version.block, publish_request.bytes)
            # Save user (with block bytes updated)
            self._store.update_user(user)

            return block.last_version()

        except Exception as excp:
            tb = traceback.format_exc()
            logger.debug(tb)
            self._rollback_transaction(excp, target_block)
            raise ServerInternalErrorException('Publish transaction failed. Please, retry')

    def create_block(self, brl, private=False):
        '''Creates a block in server due the brl and description'''
        self.security.check_create_block(brl.owner, private)
        user = self._store.read_user(brl.owner)
        try:
            block_id = user.add_block(brl)  # should fail if existing
        except DuplicateBlockException:
            logger.debug('Block %s already existing, not creating it' % brl)
            raise

        block = Block(block_id, brl)
        try:  # FIXME: better upsert?
            self._store.create_block(block, private)  # should fail if existing
        except AlreadyInStoreException:
            pass
        self._store.update_user(user)  # raise exception if not current

        return user

    def _rollback_transaction(self, excp, brl_block):
        '''rollback transaction for publish'''
        logger.warning(str(excp) + '\nRolling back publish transaction')
        self._store.rollBackBlockTransaction(brl_block)
        self._store.finishBlockTransaction(brl_block)

    def _write_resources_to_db(self, cells, contents, old_cells_ids, old_content_ids):
        '''Write cells and contents to db'''
        if old_cells_ids:
            self._store.delete_published_cells(old_cells_ids)
        if old_content_ids:
            self._store.delete_published_contents(old_content_ids)
        if cells:
            self._store.create_published_cells(cells)
        if contents:
            self._store.create_published_contents(contents)

    # @mongo_update_if_current_safe_retry
    # def __update_user_if_current(self, user):
    def _set_cell_roots(self, block, publish_request):
        '''Set cell root'''
        # Ensure here root assignment
        old_ids = {}
        deltas = block.deltas
        last_time = len(deltas) - 2

        for res in publish_request.cells:
            old_name = publish_request.renames.get_old_name(res.name.cell_name)
            old_id = block.cells.get_id(old_name, last_time)
            if old_id:
                old_ids[old_id] = res
            else:
                res.root = res.ID
        old_cells = self._store.read_published_cells(old_ids.keys())
        for old_id, old_cell in old_cells.iteritems():
            res = old_ids[old_id]
            res.root = old_cell.root

    def _in_memory_block_update(self, block, publish_request):
        '''Updates block in memory'''
        self.security.check_write_block(block.ID)
        cells, contents, old_cells_ids, old_content_ids = block.add_publication(publish_request,
                                                                                self.auth_user)
        self._set_cell_roots(block, publish_request)
        return cells, contents, old_cells_ids, old_content_ids

    def get_block_info(self, brl_block):
        '''Check if auth_user can publish a block version specified by parameter block_version
         Returns:
            BlockInfo
         '''

        try:
            self.security.check_read_block(brl_block)
        except NotInStoreException:
            # In this case, the block doesnt exist, but return information of -1 and permissions
            return self._get_new_block_info(brl_block)

        block_info = BlockInfo()
        try:
            self.security.check_write_block(brl_block)
            block_info.can_write = True
        except ForbiddenException:
            block_info.can_write = False

        try:
            block = self._store.read_block(brl_block)
            block_info.last_version = block.last_version()
            block_info.private = self.security.is_private(brl_block)
        except Exception as e:
            tb = traceback.format_exc()
            logger.debug(tb)
            logger.error("Something went wrong with %s" % e)
            raise BiiServiceException('Something went wrong')

        return block_info

    def _get_new_block_info(self, brl_block):
        '''
        Returns BlockInfo that new block would have if we publish it.
        Raises exception if block cannot be created for any reason
        '''
        last_version = BlockVersion(brl_block, -1)
        can_write = False
        try:
            self.security.check_create_block(brl_block.owner)
            can_write = True
        except ForbiddenException:
            can_write = False
        except NotInStoreException:
            raise NotFoundException("Block %s not found!" % brl_block.to_pretty())

        return BlockInfo(can_write=can_write, last_version=last_version)
class PublishService(object):
    ''' Service for publish blocks in server.'''
    def __init__(self, store, auth_user):
        self._store = store
        self.auth_user = auth_user
        self.security = Security(self.auth_user, self._store)

    def publish(self, publish_request):
        '''Performs a publication
        TIP: If we add publish_request to transaction_definition we can easily have asynchronous
        publications

        private: Only for first publication
        '''
        from biicode.server.background.enqueuer import register_publish

        if publish_request.tag == DEV:
            if not publish_request:
                raise BiiRequestErrorException(
                    'Up to date, nothing to publish')
            if publish_request.versiontag is not None:
                raise PublishException('A DEV version cannot have tag %s' %
                                       publish_request.tag)

        assert publish_request.deptable is not None

        # by default it is public
        # TODO: BLock creation is not handled in the transaction
        target_version = publish_request.parent
        user = self._store.read_user(target_version.block.owner)
        # Look if user has the block already created, because the block
        # can exist with -1 version if it has been created in web
        if target_version.block not in user.blocks.keys():
            try:
                if target_version != publish_request.parent:  # Branching
                    user = self.create_block(target_version.block,
                                             publish_request.parent,
                                             private=False)
                else:
                    user = self.create_block(target_version.block,
                                             private=False)
            except DuplicateBlockException:
                pass  # Its ok, already created

        target_block = target_version.block
        self._store.requestBlockTransaction(target_block)
        try:
            # If we can't read the block, we can't know about his existence
            self.security.check_read_block(target_block)
            self.security.check_publish_block(target_block, publish_request)
            # biiresponse.debug('Read block "%s"' % brl_block)
            block = self._store.read_block(target_block)
            (cells, contents, old_cells_ids,
             old_content_ids) = self._in_memory_block_update(
                 block, publish_request)
        except ForbiddenException:
            self._store.finishBlockTransaction(target_block)
            raise
        except PublishException as e:
            self._store.finishBlockTransaction(target_block)
            raise ServerInternalErrorException(e.message)
        except Exception as excp:
            logger.error("Exception in publish service!!: %s " % str(excp))
            tb = traceback.format_exc()
            logger.error(tb)
            self._store.finishBlockTransaction(target_block)
            raise ServerInternalErrorException()

        self._store.beginBlockTransaction(target_block, cells, contents)
        try:
            self._write_resources_to_db(cells, contents, old_cells_ids,
                                        old_content_ids)
            self._store.update_block(block)
            self._store.commitBlockTransaction(target_block)
            register_publish(self.auth_user, block.last_version())
            self._store.finishBlockTransaction(target_block)

            # Need to read user again, otherwise will raise MongoNotCurrentObjectException
            # because of double update of same memory object
            user = self._store.read_user(target_version.block.owner)
            user.add_block_size_bytes(target_version.block,
                                      publish_request.bytes)
            # Save user (with block bytes updated)
            self._store.update_user(user)

            return block.last_version()

        except Exception as excp:
            tb = traceback.format_exc()
            logger.debug(tb)
            self._rollback_transaction(excp, target_block)
            raise ServerInternalErrorException(
                'Publish transaction failed. Please, retry')

    def create_block(self, brl, private=False):
        '''Creates a block in server due the brl and description'''
        self.security.check_create_block(brl.owner, private)
        user = self._store.read_user(brl.owner)
        try:
            block_id = user.add_block(brl)  # should fail if existing
        except DuplicateBlockException:
            logger.debug('Block %s already existing, not creating it' % brl)
            raise

        block = Block(block_id, brl)
        try:  # FIXME: better upsert?
            self._store.create_block(block, private)  # should fail if existing
        except AlreadyInStoreException:
            pass
        self._store.update_user(user)  # raise exception if not current

        return user

    def _rollback_transaction(self, excp, brl_block):
        '''rollback transaction for publish'''
        logger.warning(str(excp) + '\nRolling back publish transaction')
        self._store.rollBackBlockTransaction(brl_block)
        self._store.finishBlockTransaction(brl_block)

    def _write_resources_to_db(self, cells, contents, old_cells_ids,
                               old_content_ids):
        '''Write cells and contents to db'''
        if old_cells_ids:
            self._store.delete_published_cells(old_cells_ids)
        if old_content_ids:
            self._store.delete_published_contents(old_content_ids)
        if cells:
            self._store.create_published_cells(cells)
        if contents:
            self._store.create_published_contents(contents)

    # @mongo_update_if_current_safe_retry
    # def __update_user_if_current(self, user):
    def _set_cell_roots(self, block, publish_request):
        '''Set cell root'''
        # Ensure here root assignment
        old_ids = {}
        deltas = block.deltas
        last_time = len(deltas) - 2

        for res in publish_request.cells:
            old_name = publish_request.renames.get_old_name(res.name.cell_name)
            old_id = block.cells.get_id(old_name, last_time)
            if old_id:
                old_ids[old_id] = res
            else:
                res.root = res.ID
        old_cells = self._store.read_published_cells(old_ids.keys())
        for old_id, old_cell in old_cells.iteritems():
            res = old_ids[old_id]
            res.root = old_cell.root

    def _in_memory_block_update(self, block, publish_request):
        '''Updates block in memory'''
        self.security.check_write_block(block.ID)
        cells, contents, old_cells_ids, old_content_ids = block.add_publication(
            publish_request, self.auth_user)
        self._set_cell_roots(block, publish_request)
        return cells, contents, old_cells_ids, old_content_ids

    def get_block_info(self, brl_block):
        '''Check if auth_user can publish a block version specified by parameter block_version
         Returns:
            BlockInfo
         '''

        try:
            self.security.check_read_block(brl_block)
        except NotInStoreException:
            # In this case, the block doesnt exist, but return information of -1 and permissions
            return self._get_new_block_info(brl_block)

        block_info = BlockInfo()
        try:
            self.security.check_write_block(brl_block)
            block_info.can_write = True
        except ForbiddenException:
            block_info.can_write = False

        try:
            block = self._store.read_block(brl_block)
            block_info.last_version = block.last_version()
            block_info.private = self.security.is_private(brl_block)
        except Exception as e:
            tb = traceback.format_exc()
            logger.debug(tb)
            logger.error("Something went wrong with %s" % e)
            raise BiiServiceException('Something went wrong')

        return block_info

    def _get_new_block_info(self, brl_block):
        '''
        Returns BlockInfo that new block would have if we publish it.
        Raises exception if block cannot be created for any reason
        '''
        last_version = BlockVersion(brl_block, -1)
        can_write = False
        try:
            self.security.check_create_block(brl_block.owner)
            can_write = True
        except ForbiddenException:
            can_write = False
        except NotInStoreException:
            raise NotFoundException("Block %s not found!" %
                                    brl_block.to_pretty())

        return BlockInfo(can_write=can_write, last_version=last_version)
    def test_publish(self, enqueuer):
        brl = BRLBlock('owner/user/block/branch')
        # moduleID=BlockID(UserID(123),456)
        store = Mock(MongoServerStore)
        store.read_block_permissions = Mock(
            return_value=ElementPermissions(brl, private=False))
        user = User("owner")
        user.numeric_id = 1
        user.blocks = {}
        store.read_user = Mock(return_value=user)
        block = Mock(Block)
        block.last_version.return_value = Mock(BlockVersion)
        block.add_publication.return_value = (Mock(list), Mock(list),
                                              Mock(list), Mock(list))
        block.deltas = []

        ensure = Security('authUser', store)
        ensure.check_create_block = Mock(return_value=True)
        ensure.check_write_block = Mock(return_value=True)
        ensure.check_read_block = Mock(return_value=True)

        store.read_block.return_value = block
        store.read_published_cells.return_value = {}
        p = PublishService(store, 'authUser')
        p.security = ensure

        pack = PublishRequest(BlockVersion(brl, -1))
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.contents['r1.h'] = Content(id_=None, load=Blob('hola'))
        pack.cells.append(SimpleCell('user/block/r2.h'))
        pack.contents['r2.h'] = Content(id_=None, load=Blob('hola'))
        pack.cells.append(SimpleCell('user/block/r3.h'))
        pack.contents['r3.h'] = Content(id_=None, load=Blob('hola'))
        pack.deptable = BlockVersionTable()
        p.publish(pack)

        block.add_publication.assert_called_once_with(pack, p.auth_user)
        store.update_block.assert_called_once_with(block)
        self.assertEqual(1, store.create_published_cells.call_count)
        self.assertEqual(1, store.create_published_contents.call_count)

        # Check sizes
        self.assertEquals(user.blocks_bytes, 12)  # 12 bytes "hola" * 3

        # Publish again, see the size incremented
        pack._bytes = None  # Lazy computed
        p.publish(pack)
        self.assertEquals(user.blocks_bytes,
                          24)  # 24 bytes: "hola" * 3 * 2 publications

        # Max size exceeded for user
        user.max_workspace_size = 25
        self.assertRaises(ForbiddenException, p.publish, pack)

        # Try to publish only 1 byte
        pack._bytes = None  # Lazy computed
        pack.cells = []
        pack.contents = {}
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.contents['r1.h'] = Content(id_=None, load=Blob('h'))
        p.publish(pack)