예제 #1
0
 def __init__(self, store, auth_user):
     '''param store: an instance of GenericServerStore, could be in memory for testing or
     MongoStore.
     param auth_user: the user invoking this service, must be prior authenticated by app'''
     self._store = store
     self._auth_user = auth_user
     self.security = Security(self._auth_user, self._store)
예제 #2
0
    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)
예제 #3
0
    def test_check_make_private_block(self):
        # 0. Goku is paying
        self._subscribe("goku", "enterprise_275_50_x")

        # 1. Already private.
        brl_master = BRLBlock("goku/goku/block/master")
        bper_master = ElementPermissions(brl_master, private=True)
        self.store.upsert_block_permissions(bper_master)

        # 2. Bulma cant make it private even its already private because is not an admin
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "personal_7_1_x")
        self.assertRaises(ForbiddenException, ensure.check_make_private_a_block, "goku")

        # 3. Now block is public, so bulma can't make it private
        bper_master = ElementPermissions(brl_master, private=False)
        self.store.upsert_block_permissions(bper_master)
        self.assertRaises(ForbiddenException, ensure.check_make_private_a_block, "goku")

        # 4. Grant read and write, and bulma still can't make it private
        bper_master.read.grant("bulma")
        bper_master.write.grant("bulma")
        self.store.upsert_block_permissions(bper_master)
        self.assertRaises(ForbiddenException, ensure.check_make_private_a_block, "goku")

        # 5. Make bulma an admin, now bulma can make it private
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_make_private_a_block("goku")

        # 6. Bulma cant make private the block when suscription is not active
        self._subscribe("goku", "free")
        self.assertRaises(ForbiddenException, ensure.check_make_private_a_block, "goku")
예제 #4
0
    def test_check_make_public_block(self):

        # 1. Already public.
        brl_master = BRLBlock("goku/goku/block/master")
        bper_master = ElementPermissions(brl_master, private=False)
        self.store.upsert_block_permissions(bper_master)

        # 2. Bulma cant make it public even its already public
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_make_private_a_block, "goku")

        # 3. Now block is public, so bulma cant make it public
        bper_master = ElementPermissions(brl_master, private=True)
        self.store.upsert_block_permissions(bper_master)
        self.assertRaises(ForbiddenException, ensure.check_make_public_a_block, "goku")

        # 4. Grant read and write, and bulma still cant make it public
        bper_master.read.grant("bulma")
        bper_master.write.grant("bulma")
        self.store.upsert_block_permissions(bper_master)
        self.assertRaises(ForbiddenException, ensure.check_make_public_a_block, "goku")

        # 5. Make bulma an admin, now bulma can make it public
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_make_public_a_block("goku")

        # 5. Bulma can make public the block even when suscription is not active
        self._subscribe("bulma", "free")
        goku = self.store.read_user("goku")
        self.store.update_user(goku)
        ensure.check_make_public_a_block("goku")
예제 #5
0
    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)
예제 #6
0
    def test_check_read_user_subscription(self):
        # Can do it himself (always, event not paying) and not their administrators
        ensure = Security("goku", self.store)
        self._subscribe("goku", "enterprise_275_50_x")
        ensure.check_read_user_subscription("goku")

        self._subscribe("goku", "free")
        ensure.check_read_user_subscription("goku")

        # Other admin-granted  user cant do it
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)

        ensure = Security("bulma", self.store)
        ensure.check_read_user_subscription("goku")
예제 #7
0
    def test_update_user(self):
        # Can do it himself and their administrators

        ensure = Security("goku", self.store)
        self._subscribe("goku", "enterprise_275_50_x")
        ensure.check_update_user("goku")

        self._subscribe("goku", "free")
        ensure.check_update_user("goku")

        # Other admin-granted user can't do it
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)

        ensure = Security("bulma", self.store)
        ensure.check_update_user("goku")
    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)
class ReferenceTranslatorService(object):
    def __init__(self, store, auth_user):
        self._store = store
        self.security = Security(auth_user, self._store)

    def get_published_resources(self, references):
        result = ReferencedResources()
        for block_version, cell_names in references.iteritems():
            try:
                self.security.check_read_block(block_version.block)
                block = self._store.read_block(block_version.block)
                cell_ids = block.cells.get_ids(cell_names, block_version.time)
                content_ids = block.contents.get_ids(cell_names, block_version.time)
                cells = self._store.read_published_cells(cell_ids.values())
                contents = self._store.read_published_contents(content_ids.values())
                for name, rID in cell_ids.iteritems():
                    if name in content_ids:
                        cid = content_ids[name]
                        cid = contents[cid]
                    else:
                        cid = None  # Virtual resource
                    result[block_version][name] = Resource(cells[rID], cid)
            except (ForbiddenException, NotInStoreException):
                pass
        return result

    def get_published_min_refs(self, references):
        """returns the minimum information required to perform a compatibility check for those
        references. This method is currently used just by CompatibilityClosureBuilder

        param references: {block_version: set(cell_names)}
        return: {block_version: {cell_name: (cell_id, content_id), root_id, [deps blockcellnames]}}
        """
        result = defaultdict(dict)
        for block_version, cell_names in references.iteritems():
            try:
                self.security.check_read_block(block_version.block)
                block = self._store.read_block(block_version.block)
                cell_ids = block.cells.get_ids(cell_names, block_version.time)
                content_ids = block.contents.get_ids(cell_names, block_version.time)
                cells = self._store.read_min_cells(cell_ids.values())
                # This cells are {cellID: (rootID, dep_block_names)}
                for cell_name, cell_id in cell_ids.iteritems():
                    content_id = content_ids.get(cell_name)  # None if Virtual resource
                    root_id, deps = cells.get(cell_id, (None, None))
                    if root_id is not None:
                        result[block_version][cell_name] = ((cell_id, content_id), root_id, deps)
            except (ForbiddenException, NotInStoreException):
                pass

        return result

    def get_dep_table(self, block_version):
        self.security.check_read_block(block_version.block)
        block = self._store.read_block(block_version.block)
        table = block.dep_tables.find(block_version.time)
        return table
예제 #10
0
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)
예제 #11
0
    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)
예제 #12
0
    def test_read_block(self):

        # 1. Check we can always access a public block
        brl = BRLBlock("goku/goku/block/master")
        bper = ElementPermissions(brl, private=False)
        self.store.upsert_block_permissions(bper)

        ensure = Security("freezer", self.store)
        self._subscribe("freezer", "enterprise_275_50_x")
        ensure.check_read_block(brl)

        # Even owner is not paying
        self._subscribe("freezer", "free")
        ensure.check_read_block(brl)
        self._subscribe("freezer", "enterprise_275_50_x")

        # 2. Check we can read a private block due to being the owner
        bper = ElementPermissions(brl, private=True)
        self.store.upsert_block_permissions(bper)

        ensure = Security("goku", self.store)
        self._subscribe("freezer", "enterprise_275_50_x")
        ensure.check_read_block(brl)

        # 4. Check we can read the block even subscription is not valid
        self._subscribe("freezer", "free")
        ensure.check_read_block(brl)

        # 5. A user without read permissions cant read the block
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_read_block, brl)

        # 6. Until is granted as administrator or read granted
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_read_block(brl)
        goku.administrators.revoke("bulma")
        self.store.update_user(goku)
        self.assertRaises(ForbiddenException, ensure.check_read_block, brl)

        bper = ElementPermissions(brl, private=True)
        bper.read.grant("bulma")
        self.store.upsert_block_permissions(bper)
        ensure.check_read_block(brl)
        bper.read.remove("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_read_block, brl)
예제 #13
0
 def __init__(self, store, auth_user):
     self._store = store
     self._auth_user = auth_user
     self.security = Security(self._auth_user, self._store)
     self.translator = ReferenceTranslatorService(self._store,
                                                  self._auth_user)
예제 #14
0
class FindService(object):
    MAX_HYP = 10

    def __init__(self, store, auth_user):
        self._store = store
        self._auth_user = auth_user
        self.security = Security(self._auth_user, self._store)
        self.translator = ReferenceTranslatorService(self._store,
                                                     self._auth_user)

    def find(self, request, biiout):
        '''
        Params:
            request: FinderRequest
            biiout: biiout
        Rerturns: FinderResult
        '''
        if not request:
            raise ValueError('The find request is empty, nothing to find')

        logger.debug('---------FinderRequest ------------\n%s' % str(request))
        result = FinderResult()
        # Copy unresolved and remove it if find the dependence
        result.unresolved = copy(request.unresolved)

        hypothesis = self._get_hypothesis(request, biiout)
        if not hypothesis:
            biiout.info("No block candidates found")
            return result

        biiout.info("Analyzing compatibility for found dependencies... ")
        '''# primitive combinator variant
        analyzer = CompatibilityAnalyzer(self._store, self._auth_user)
        analysis_result = analyzer.solve(hypothesis)

        # standard constraint variant
        csp = CSPExact(hypothesis, None)
        csp.solveCSP()
        analysis_result = csp.getCompatibleSol()
        logger.info(csp.print_info())'''

        # iterative deepening variant
        it = IterDeep(hypothesis, None, None)
        sol_found, analysis_result = it.start()
        if sol_found:
            logger.info("sol found: {0} iter".format(it.num_iter))

        if analysis_result is None:
            biiout.error("Can't find a compatible solution")
            return result

        self._update_result(analysis_result, request, result, biiout)
        if not result.unresolved:
            if result.resolved:
                biiout.info('All dependencies resolved')
            elif not result.updated:
                biiout.info('Everything was up to date')
        logger.debug('Result %s' % result)
        return result

    def _get_hypothesis(self, request, biiresponse):
        hypothesis = []
        if request.find:
            # group unresolved declarations by module
            possible_blocks = request.possible_blocks()
            logger.debug('Possible blocks %s' % possible_blocks)
            for block_name, decls in possible_blocks.items():
                hyp = self._compute_new(block_name, decls, request.policy,
                                        request.block_names, biiresponse)
                if len(hyp) > 0:  # Don't append []
                    hypothesis.append(hyp)

        existing_hypothesis = self._compute_existing(request, biiresponse)
        if len(existing_hypothesis) > 0:
            hypothesis.extend(existing_hypothesis)
        logger.debug('Hypothesis %s' % hypothesis)
        return hypothesis

    def _update_result(self, analysis_result, request, result, biiout):
        #existing = {version.block: version.time for version in request.existing}
        for elem in analysis_result:
            version = elem.block_version
            for declaration, refs in elem.dep_dict.iteritems():
                if declaration in request.unresolved:
                    biiout.debug("Resolved declaration %s" % str(declaration))
                    result.resolved[version][declaration] = refs
                elif version not in request.existing:
                    biiout.info("Block %s updated to version %s" %
                                (version.block, version.time))
                    result.updated[version][declaration] = refs

            # Remove cells from finder response unresolved
            result.unresolved.difference_update(elem.dep_dict.keys())

    def _compute_existing(self, request, biiout):
        '''return a list of list of hypothesis for already defined (existing)
        dependencies'''
        result = []
        for block_version, deps in request.existing.iteritems():
            if request.update or request.downgrade or request.modify:
                hypothesis = self._compute_modify(block_version, deps, request,
                                                  biiout)
            else:
                hypothesis = []
            hypothesis.append(
                Hypothesis(block_version, request.existing[block_version],
                           self.translator, request.block_names, biiout))
            result.append(hypothesis)
        return result

    def _compute_modify(self, block_version, dependencies, request, biiout):
        '''
        Params:
            block_version: Version to which dependencies are currently resolved to
            dependencies: {Declaration: set(BlockCellName)}
            request: FinderRequest
        '''
        brl_block = block_version.block
        time = block_version.time

        # First, compute all block candidates that have to be considered
        block_candidates = {brl_block}

        # Remove those not wanted by our policy
        policy = request.policy
        block_candidates = policy.filter(block_candidates)

        current_block = self._store.read_block(brl_block)
        original_date = current_block.deltas[time].date
        delta_versions = self._filter_by_policy(block_candidates, policy,
                                                biiout, original_date, request)
        logger.debug("The heap is %s" % delta_versions)
        hypothesis = self._define_hypothesis(delta_versions, dependencies,
                                             request.block_names, biiout,
                                             block_version)
        return hypothesis

    def _match_declarations(self, decls, block, snap, cur_version, version):
        '''
        Params:
            decls: Current declarations for given block
            block: Block to match
            snap: dict {CellName => ID} for new version
            cur_version: Current BlockVersion that decls are resolved to
            version: New BlockVersion to evaluate
        Return:
            all_found: boolean
            names_total: set(BlockCellName)
            deps_dict: Dict {Declaration => set(BlockCellName)}
        '''
        all_found = True
        deps_dict = {}
        names_total = set()
        block_name = block.ID.block_name

        for decl in decls:
            matchable_names = snap.keys()
            renames = {}
            if cur_version:
                renames = block.get_renames(cur_version.time, version.time)
                matchable_names.extend(
                    renames.keys())  # Adding old names so decls matches

            names = decl.match([block_name + name for name in matchable_names])
            names = set([
                n if n.cell_name in snap.keys() else
                (block_name + renames[n.cell_name]) for n in names
            ])  # Apply renames
            if names:
                # In case of renames here we will have a mismatch between declaration and cell_name
                # it will be corrected by client by updating declaration when it detects such
                # mismatch
                deps_dict[decl] = names
                names_total.update(names)
            else:
                all_found = False
                break
        return all_found, names_total, deps_dict

    def _define_hypothesis(self,
                           delta_versions,
                           decls,
                           existing_block_names,
                           biiresponse,
                           cur_version=None):
        '''
        Parameters:
            delta_versions: [(delta, block_version)], prioritized set of accepted hypothesis
            decls: {Declaration: set(BlockCellName)}
            existing_block_names = set(BlockName)
            cur_version: Current version that decls are resolved to

        Returns: list of hypothesis that match the required decls
        '''
        result = []

        #repeated = set()
        #previous = None
        for _, version in delta_versions:
            logger.debug('Analyzing hypothesis %s' % str(version))
            block = self._store.read_block(version.block)
            snap = block.cells.get_all_ids(version.time)
            #logger.debug('Current snap %s' % snap)

            all_found, names_total, deps_dict = self._match_declarations(
                decls, block, snap, cur_version, version)
            if not all_found:
                biiresponse.debug(
                    'Version %s discarded, only contains files for declarations %s'
                    % (str(version), deps_dict.keys()))
                continue

            # Store the current IDs and dep table
            #snap_contents = block.contents.get_ids(version.time)
            #cell_ids = {snap[k.cell_name] for k in names_total}
            #content_ids = {snap_contents[k.cell_name] for k in names_total if
            #               k.cell_name in snap_contents}
            #dep_table = block.dep_tables.floor(version.time)
            #current = cell_ids, content_ids, dep_table
            # Only if the current option is different to the previous one
            # we dont want to check the same option twice
            #if previous != current and deps_dict:
            logger.debug('Building hypothesis for %s with %s' %
                         (version, deps_dict))
            # logger.debug('ref_dict %s' % ref_dict)
            hyp = Hypothesis(version, deps_dict, self.translator,
                             existing_block_names, biiresponse)
            result.append(hyp)
            #previous = current
            # FIXME: now the limit of hypothesis is hardwired
            if len(result) >= FindService.MAX_HYP:
                break
        return result

    def _filter_by_policy(self,
                          block_candidates,
                          policy,
                          biiresponse,
                          original_date=None,
                          request=None):
        '''computes list of (block_delta, block_version) for each block candidate'''
        delta_versions = []
        for block_candidate in block_candidates:
            self.security.check_read_block(block_candidate)
            biiresponse.info("Block candidate: %s" % str(block_candidate))
            block = self._store.read_block(block_candidate)

            # from last to 0, backwards
            for num_version in range(len(block.deltas) - 1, -1, -1):
                tag = block.deltas[num_version].tag
                date = block.deltas[num_version].date
                if request:
                    if not request.downgrade and date <= original_date:
                        continue
                version = BlockVersion(block.ID, num_version)
                ev = policy.evaluate(version, tag)
                if ev:
                    heappush(delta_versions, ((-date, -num_version), version))
                    biiresponse.info("\tVersion %s (%s) valid" %
                                     (version, tag))
                else:
                    biiresponse.info("\tVersion %s (%s) discarded" %
                                     (version, tag))

        return delta_versions

    def _compute_new(self, block_name, decls, policy, existing_block_names,
                     biiresponse):
        try:
            biiresponse.info("Looking for %s..." % block_name)
            # branches = self._store.read_tracks(block_name)
            # branches.get_blocks()
            block_candidates = [
                block_name + BranchName("%s/master" % block_name.user)
            ]
            block_candidates = policy.filter(block_candidates)
            delta_versions = self._filter_by_policy(block_candidates, policy,
                                                    biiresponse)
            logger.debug("The heap is %s" % delta_versions)
            result = self._define_hypothesis(delta_versions, decls,
                                             existing_block_names, biiresponse)
            return result
        except ForbiddenException:  # Propagate forbidden to client
            raise
        except NotInStoreException:
            biiresponse.warn("Can't find block candidate for: %s" %
                             (str(block_name)))
            return []
        except Exception:
            biiresponse.error("Fatal error in server while reading %s" %
                              block_name)
            logger.error(traceback.format_exc())
            return []
예제 #15
0
    def test_check_publish_block(self):
        # 1. Onwer can write the block if its private
        brl = BRLBlock("goku/goku/block/master")
        self._add_block_to_user("goku", brl, True)

        ensure = Security("goku", self.store)
        self._subscribe("goku", "enterprise_275_50_x")

        pack = PublishRequest(BlockVersion(brl, -1))
        pack.cells.append(SimpleCell('user/block/r1.h'))
        pack.contents['r1.h'] = Content(id_=None, load=Blob('hola'))
        pack.versiontag = 'mytag'

        ensure.check_publish_block(brl, pack)

        # If the owner is not paying he can't write
        ensure = Security("goku", self.store)
        self._subscribe("goku", "free")
        self.assertRaises(ForbiddenException, ensure.check_publish_block, brl, pack)
        self._subscribe("goku", "enterprise_275_50_x")

        # 1b. But other not granted user can't write
        ensure = Security("freezer", self.store)
        self._subscribe("freezer", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_publish_block, brl, pack)

        # 2. If bulma is granted as administrator or write he can write
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "enterprise_275_50_x")
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_publish_block(brl, pack)
        goku.administrators.revoke("bulma")
        self.store.update_user(goku)
        self.assertRaises(ForbiddenException, ensure.check_publish_block, brl, pack)

        bper = ElementPermissions(brl, private=True)
        bper.write.grant("bulma")
        self.store.upsert_block_permissions(bper)
        ensure.check_publish_block(brl, pack)
        bper.write.remove("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_publish_block, brl, pack)

        # 3. If we give read permissions only, user cant write
        bper = ElementPermissions(brl, private=True)
        bper.read.grant("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_publish_block, brl, pack)
예제 #16
0
 def get_renames(self, brl_block, t1, t2):
     '''Gets 2 BlockVersion ([0],[1]) in a list and returns the renames'''
     security = Security(self._auth_user, self._store)
     security.check_read_block(brl_block)
     block = self._store.read_block(brl_block)
     return block.get_renames(t1, t2)
예제 #17
0
    def test_write_block(self):
        # 1. Onwer can write the block if its private
        brl = BRLBlock("goku/goku/block/master")
        self._add_block_to_user("goku", brl, True)

        ensure = Security("goku", self.store)
        self._subscribe("goku", "enterprise_275_50_x")
        ensure.check_write_block(brl)

        # If the owner is not paying he can't write
        ensure = Security("goku", self.store)
        self._subscribe("goku", "free")
        self.assertRaises(ForbiddenException, ensure.check_write_block, brl)
        self._subscribe("goku", "enterprise_275_50_x")

        # 1b. But other not granted user can't write
        ensure = Security("freezer", self.store)
        self._subscribe("freezer", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_write_block, brl)

        # 2. If bulma is granted as administrator or write he can write
        ensure = Security("bulma", self.store)
        self._subscribe("bulma", "enterprise_275_50_x")
        goku = self.store.read_user("goku")
        goku.administrators.grant("bulma")
        self.store.update_user(goku)
        ensure.check_write_block(brl)
        goku.administrators.revoke("bulma")
        self.store.update_user(goku)
        self.assertRaises(ForbiddenException, ensure.check_write_block, brl)

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

        # 3. If we give read permissions only, user cant write
        bper = ElementPermissions(brl, private=True)
        bper.read.grant("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_write_block, brl)
예제 #18
0
    def test_change_subscription(self):
        # Create users for test
        self.store.create_user(User("cooper"))
        ensure = Security("cooper", self.store)
        self._subscribe("cooper", "startup_35_5_x")

        # Coop has a private block
        private_brl = BRLBlock("cooper/cooper/private_block/master")
        self._add_block_to_user("cooper", private_brl, True)

        for tmp in xrange(4):
            new_user = BRLUser("user%s" % tmp)
            ensure.check_grant_read_or_write_permissions_to(new_user, private_brl)
            bper = self.store.read_block_permissions(private_brl)
            bper.write.grant(new_user)
            self.store.upsert_block_permissions(bper)

        # Try to downgrade to personal plan
        with self.assertRaisesRegexp(ForbiddenException, "You are currently using 4 users, "
                                                         "reduce to 1 before plan downgrade"):
            ensure.check_can_change_current_subscription("cooper", "personal_7_1_x")

        # Remove collaborators to 1
        for tmp in xrange(3):
            new_user = BRLUser("user%s" % tmp)
            ensure.check_grant_read_or_write_permissions_to(new_user, private_brl)
            bper = self.store.read_block_permissions(private_brl)
            bper.write.revoke(new_user)
            self.store.upsert_block_permissions(bper)

        # Try to downgrade to personal plan
        ensure.check_can_change_current_subscription("cooper", "personal_7_1_x")

        # Try to downgrade to free plan
        with self.assertRaisesRegexp(ForbiddenException, "You are currently using 1 users, "
                                                         "reduce to 0 before plan downgrade"):
            ensure.check_can_change_current_subscription("cooper", "free")

        # Remove last collaborator
        bper = self.store.read_block_permissions(private_brl)
        bper.write.revoke("user3")
        self.store.upsert_block_permissions(bper)

        with self.assertRaisesRegexp(ForbiddenException, "You have 1 private blocks, "
                                                         "reduce it to 0 before plan downgrade"):
            ensure.check_can_change_current_subscription("cooper", "free")
예제 #19
0
    def test_delete_block(self):
        # First create master block permissions. Public
        brl = BRLBlock("goku/goku/block/master")
        bper_master = ElementPermissions(brl, private=True)
        self.store.upsert_block_permissions(bper_master)

        # 1. Owner can always delete a private block, even if i am not paying
        ensure = Security("goku", self.store)
        self._subscribe("goku", "enterprise_275_50_x")
        ensure.check_delete_block(brl)

        self._subscribe("freezer", "free")
        ensure.check_delete_block(brl)

        # 2. Other user can't delete block
        ensure = Security("freezer", self.store)
        self._subscribe("freezer", "enterprise_275_50_x")
        self.assertRaises(ForbiddenException, ensure.check_delete_block, brl)

        # 3. A user with read and write permissions cant delete
        ensure = Security("bulma", self.store)
        bper = ElementPermissions(brl, private=True)
        bper.write.grant("bulma")
        bper.read.grant("bulma")
        self.store.upsert_block_permissions(bper)
        self.assertRaises(ForbiddenException, ensure.check_delete_block, brl)

        # 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_delete_block(brl)
예제 #20
0
    def test_subscription_limits(self):

        # Create users for test
        self.store.create_user(User("cooper"))
        self.store.create_user(User("bob"))
        self.store.create_user(User("lady_lug"))

        # Coop has a private block and a public block
        public_brl = BRLBlock("cooper/cooper/public_block/master")
        private_brl = BRLBlock("cooper/cooper/private_block/master")
        self._add_block_to_user("cooper", public_brl, False)
        self._add_block_to_user("cooper", private_brl, True)

        # 1 contributor and unlimited private blocks
        self._subscribe("cooper", "personal_7_1_x")

        # Add an administrator
        ensure = Security("cooper", self.store)
        ensure.check_grant_administrator_for("cooper", "bob")
        cooper = self.store.read_user("cooper")
        cooper.administrators.grant("bob")
        self.store.update_user(cooper)

        # Try to add another one, it must fail
        self.assertRaises(PlanUpgradeNeeded,
                          ensure.check_grant_administrator_for,
                          "cooper", "lady_lug")

        # Try to add write permissions to a public block. Its ok
        ensure.check_grant_read_or_write_permissions_to("lady_lug", public_brl)

        # Try to add write permissions to a private block, but a bob (already admin)
        ensure.check_grant_read_or_write_permissions_to("bob", private_brl)

        # Try to add write permissions to a private block. It must fail
        self.assertRaises(PlanUpgradeNeeded,
                          ensure.check_grant_read_or_write_permissions_to,
                          "lady_lug", private_brl)

        # Remove Adminsitrator, try to add write permissions in a private block. Its ok
        cooper = self.store.read_user("cooper")
        cooper.administrators.revoke("bob")
        self.store.update_user(cooper)
        ensure.check_grant_read_or_write_permissions_to("lady_lug", private_brl)
        cooper.administrators.grant("lady_lug")
        self.store.update_user(cooper)

        # Subscribe to a bigger plan and check limits
        self._subscribe("cooper", "startup_35_5_x")
        # Add 4 more
        for tmp in xrange(4):
            new_user = BRLUser("user%s" % tmp)
            ensure.check_grant_read_or_write_permissions_to(new_user, private_brl)
            bper = self.store.read_block_permissions(private_brl)
            bper.write.grant(new_user)
            self.store.upsert_block_permissions(bper)

        # The sixth must fail
        self.assertRaises(PlanUpgradeNeeded,
                          ensure.check_grant_read_or_write_permissions_to,
                          "new_user", private_brl)

        # unless user is already a contributor
        ensure.check_grant_read_or_write_permissions_to("lady_lug", private_brl)
예제 #21
0
 def test_anonymous_operations(self):
     ensure = Security(None, self.store)
     ensure.check_read_block(self.public_brl)
예제 #22
0
class FindService(object):
    MAX_HYP = 10

    def __init__(self, store, auth_user):
        self._store = store
        self._auth_user = auth_user
        self.security = Security(self._auth_user, self._store)
        self.translator = ReferenceTranslatorService(self._store, self._auth_user)

    def find(self, request, biiout):
        '''
        Params:
            request: FinderRequest
            biiout: biiout
        Rerturns: FinderResult
        '''
        if not request:
            raise ValueError('The find request is empty, nothing to find')

        logger.debug('---------FinderRequest ------------\n%s' % str(request))
        result = FinderResult()
        # Copy unresolved and remove it if find the dependence
        result.unresolved = copy(request.unresolved)

        hypothesis = self._get_hypothesis(request, biiout)
        if not hypothesis:
            biiout.info("No block candidates found")
            return result

        biiout.info("Analyzing compatibility for found dependencies... ")
        '''# primitive combinator variant
        analyzer = CompatibilityAnalyzer(self._store, self._auth_user)
        analysis_result = analyzer.solve(hypothesis)

        # standard constraint variant
        csp = CSPExact(hypothesis, None)
        csp.solveCSP()
        analysis_result = csp.getCompatibleSol()
        logger.info(csp.print_info())'''

        # iterative deepening variant
        it = IterDeep(hypothesis, None, None)
        sol_found, analysis_result = it.start()
        if sol_found:
            logger.info("sol found: {0} iter".format(it.num_iter))

        if analysis_result is None:
            biiout.error("Can't find a compatible solution")
            return result

        self._update_result(analysis_result, request, result, biiout)
        if not result.unresolved:
            if result.resolved:
                biiout.info('All dependencies resolved')
            elif not result.updated:
                biiout.info('Everything was up to date')
        logger.debug('Result %s' % result)
        return result

    def _get_hypothesis(self, request, biiresponse):
        hypothesis = []
        if request.find:
            # group unresolved declarations by module
            possible_blocks = request.possible_blocks()
            logger.debug('Possible blocks %s' % possible_blocks)
            for block_name, decls in possible_blocks.items():
                hyp = self._compute_new(block_name, decls, request.policy,
                                        request.block_names, biiresponse)
                if len(hyp) > 0:  # Don't append []
                    hypothesis.append(hyp)

        existing_hypothesis = self._compute_existing(request, biiresponse)
        if len(existing_hypothesis) > 0:
            hypothesis.extend(existing_hypothesis)
        logger.debug('Hypothesis %s' % hypothesis)
        return hypothesis

    def _update_result(self, analysis_result, request, result, biiout):
        #existing = {version.block: version.time for version in request.existing}
        for elem in analysis_result:
            version = elem.block_version
            for declaration, refs in elem.dep_dict.iteritems():
                if declaration in request.unresolved:
                    biiout.debug("Resolved declaration %s" % str(declaration))
                    result.resolved[version][declaration] = refs
                elif version not in request.existing:
                    biiout.info("Block %s updated to version %s"
                                      % (version.block, version.time))
                    result.updated[version][declaration] = refs

            # Remove cells from finder response unresolved
            result.unresolved.difference_update(elem.dep_dict.keys())

    def _compute_existing(self, request, biiout):
        '''return a list of list of hypothesis for already defined (existing)
        dependencies'''
        result = []
        for block_version, deps in request.existing.iteritems():
            if request.update or request.downgrade or request.modify:
                hypothesis = self._compute_modify(block_version, deps, request, biiout)
            else:
                hypothesis = []
            hypothesis.append(Hypothesis(block_version, request.existing[block_version],
                                         self.translator, request.block_names, biiout))
            result.append(hypothesis)
        return result

    def _compute_modify(self, block_version, dependencies, request, biiout):
        '''
        Params:
            block_version: Version to which dependencies are currently resolved to
            dependencies: {Declaration: set(BlockCellName)}
            request: FinderRequest
        '''
        brl_block = block_version.block
        time = block_version.time

        # First, compute all block candidates that have to be considered
        block_candidates = {brl_block}

        # Remove those not wanted by our policy
        policy = request.policy
        block_candidates = policy.filter(block_candidates)

        current_block = self._store.read_block(brl_block)
        original_date = current_block.deltas[time].date
        delta_versions = self._filter_by_policy(block_candidates, policy, biiout,
                                                original_date, request)
        logger.debug("The heap is %s" % delta_versions)
        hypothesis = self._define_hypothesis(delta_versions, dependencies, request.block_names,
                                             biiout, block_version)
        return hypothesis

    def _match_declarations(self, decls, block, snap, cur_version, version):
        '''
        Params:
            decls: Current declarations for given block
            block: Block to match
            snap: dict {CellName => ID} for new version
            cur_version: Current BlockVersion that decls are resolved to
            version: New BlockVersion to evaluate
        Return:
            all_found: boolean
            names_total: set(BlockCellName)
            deps_dict: Dict {Declaration => set(BlockCellName)}
        '''
        all_found = True
        deps_dict = {}
        names_total = set()
        block_name = block.ID.block_name

        for decl in decls:
            matchable_names = snap.keys()
            renames = {}
            if cur_version:
                renames = block.get_renames(cur_version.time, version.time)
                matchable_names.extend(renames.keys())  # Adding old names so decls matches

            names = decl.match([block_name + name for name in matchable_names])
            names = set([n if n.cell_name in snap.keys()
                         else (block_name + renames[n.cell_name]) for n in names])  # Apply renames
            if names:
                # In case of renames here we will have a mismatch between declaration and cell_name
                # it will be corrected by client by updating declaration when it detects such
                # mismatch
                deps_dict[decl] = names
                names_total.update(names)
            else:
                all_found = False
                break
        return all_found, names_total, deps_dict

    def _define_hypothesis(self, delta_versions, decls, existing_block_names, biiresponse,
                           cur_version=None):
        '''
        Parameters:
            delta_versions: [(delta, block_version)], prioritized set of accepted hypothesis
            decls: {Declaration: set(BlockCellName)}
            existing_block_names = set(BlockName)
            cur_version: Current version that decls are resolved to

        Returns: list of hypothesis that match the required decls
        '''
        result = []

        #repeated = set()
        #previous = None
        for _, version in delta_versions:
            logger.debug('Analyzing hypothesis %s' % str(version))
            block = self._store.read_block(version.block)
            snap = block.cells.get_all_ids(version.time)
            #logger.debug('Current snap %s' % snap)

            all_found, names_total, deps_dict = self._match_declarations(decls, block, snap,
                                                                         cur_version, version)
            if not all_found:
                biiresponse.debug('Version %s discarded, only contains files for declarations %s'
                                  % (str(version), deps_dict.keys()))
                continue

            # Store the current IDs and dep table
            #snap_contents = block.contents.get_ids(version.time)
            #cell_ids = {snap[k.cell_name] for k in names_total}
            #content_ids = {snap_contents[k.cell_name] for k in names_total if
            #               k.cell_name in snap_contents}
            #dep_table = block.dep_tables.floor(version.time)
            #current = cell_ids, content_ids, dep_table
            # Only if the current option is different to the previous one
            # we dont want to check the same option twice
            #if previous != current and deps_dict:
            logger.debug('Building hypothesis for %s with %s' % (version, deps_dict))
            # logger.debug('ref_dict %s' % ref_dict)
            hyp = Hypothesis(version, deps_dict, self.translator, existing_block_names,
                             biiresponse)
            result.append(hyp)
            #previous = current
            # FIXME: now the limit of hypothesis is hardwired
            if len(result) >= FindService.MAX_HYP:
                break
        return result

    def _filter_by_policy(self, block_candidates, policy, biiresponse,
                          original_date=None, request=None):
        '''computes list of (block_delta, block_version) for each block candidate'''
        delta_versions = []
        for block_candidate in block_candidates:
            self.security.check_read_block(block_candidate)
            biiresponse.info("Block candidate: %s" % str(block_candidate))
            block = self._store.read_block(block_candidate)

            # from last to 0, backwards
            for num_version in range(len(block.deltas) - 1, -1, -1):
                tag = block.deltas[num_version].tag
                date = block.deltas[num_version].date
                if request:
                    if not request.downgrade and date <= original_date:
                        continue
                version = BlockVersion(block.ID, num_version)
                ev = policy.evaluate(version, tag)
                if ev:
                    heappush(delta_versions, ((-date, -num_version), version))
                    biiresponse.info("\tVersion %s (%s) valid" % (version, tag))
                else:
                    biiresponse.info("\tVersion %s (%s) discarded" % (version, tag))

        return delta_versions

    def _compute_new(self, block_name, decls, policy, existing_block_names, biiresponse):
        try:
            biiresponse.info("Looking for %s..." % block_name)
            # branches = self._store.read_tracks(block_name)
            # branches.get_blocks()
            block_candidates = [block_name + BranchName("%s/master" % block_name.user)]
            block_candidates = policy.filter(block_candidates)
            delta_versions = self._filter_by_policy(block_candidates, policy, biiresponse)
            logger.debug("The heap is %s" % delta_versions)
            result = self._define_hypothesis(delta_versions, decls,
                                             existing_block_names, biiresponse)
            return result
        except ForbiddenException:  # Propagate forbidden to client
            raise
        except NotInStoreException:
            biiresponse.warn("Can't find block candidate for: %s" % (str(block_name)))
            return []
        except Exception:
            biiresponse.error("Fatal error in server while reading %s" % block_name)
            logger.error(traceback.format_exc())
            return []
예제 #23
0
 def __init__(self, store, auth_user):
     self._store = store
     self._auth_user = auth_user
     self.security = Security(self._auth_user, self._store)
     self.translator = ReferenceTranslatorService(self._store, self._auth_user)
예제 #24
0
 def __init__(self, store, auth_user):
     self._store = store
     self.auth_user = auth_user
     self.security = Security(self.auth_user, self._store)
예제 #25
0
class BiiService(BiiAPI):
    '''Realization of the BiiAPI, the main entry point for functionality for server remote calls'''

    def __init__(self, store, auth_user):
        '''param store: an instance of GenericServerStore, could be in memory for testing or
        MongoStore.
        param auth_user: the user invoking this service, must be prior authenticated by app'''
        self._store = store
        self._auth_user = auth_user
        self.security = Security(self._auth_user, self._store)

    def get_server_info(self):
        ''' Gets the server info'''
        from biicode.server.conf import BII_DOWNLOAD_URL
        from biicode.server.conf import BII_GREET_MSG
        si = ServerInfo(message=BII_GREET_MSG)
        si.last_compatible = BII_LAST_COMPATIBLE_CLIENT
        si.download_url = BII_DOWNLOAD_URL
        return si

    def publish(self, publish_request):
        ''' Publish in bii server'''
        p = PublishService(self._store, self._auth_user)
        return p.publish(publish_request)

    def get_block_info(self, brl_block):
        ''' Read the block and get a BlockInfo object'''
        p = PublishService(self._store, self._auth_user)
        return p.get_block_info(brl_block)

    def get_version_delta_info(self, block_version):
        """ Read the delta info of a given block version
        Raises: NotFoundException if version does not exist or is incongruent
        """
        assert block_version.time is not None
        try:
            # FIXME: Optimize, reading all block only for get last version
            self.security.check_read_block(block_version.block)
            block = self._store.read_block(block_version.block)
            if block_version.time > -1:
                return block.deltas[block_version.time]
            else:
                return None
        except NotInStoreException:
            raise NotFoundException("Block %s not found!" % block_version.block.to_pretty())
        except IndexError:
            raise NotFoundException("Block version %s not found!" % str(block_version))

    def get_version_by_tag(self, brl_block, version_tag):
        """Given a BlockVersion that has a tag but not a time returns a complete BlockVersion"""
        assert version_tag is not None
        try:
            self.security.check_read_block(brl_block)
            block = self._store.read_block(brl_block)
            for time, delta in reversed(list(enumerate(block.deltas))):
                if delta.versiontag == version_tag:
                    return BlockVersion(brl_block, time, version_tag)
            raise NotFoundException("Block version %s: @%s not found!"
                                    % (brl_block.to_pretty(), version_tag))
        except NotInStoreException:
            raise NotFoundException("Block %s not found!" % str(brl_block))

    def get_published_resources(self, reference_dict):
        ''' Get the resources by their brl'''
        r = ReferenceTranslatorService(self._store, self._auth_user)
        return r.get_published_resources(reference_dict)

    def get_dep_table(self, block_version):
        ''' Get the dependence table for this block version'''
        assert block_version.time is not None
        try:
            r = ReferenceTranslatorService(self._store, self._auth_user)
            return r.get_dep_table(block_version)
        except NotInStoreException:
            raise NotFoundException("Block %s not found!" % str(block_version.block))

    def get_cells_snapshot(self, block_version):
        ''' Gets all cell names for an specific block version '''
        assert block_version.time is not None
        brl_block = block_version.block

        try:
            self.security.check_read_block(brl_block)  # Security first, always
            block = self._store.read_block(brl_block)

            if block_version.time > len(block.deltas) - 1:
                raise NotFoundException("There is no published version %d of %s\n" %
                                        (block_version.time, block_version.block))

            tmp = block.cells.get_all_ids(block_version.time)  # Dict {cell_name: ID}
            return tmp.keys()

        except NotInStoreException:
            raise NotFoundException("Block version %s not found!\n" % str(block_version))

    def find(self, finder_request, response):
        ''' Find remote dependences '''
        store = MemServerStore(self._store)
        f = FindService(store, self._auth_user)
        return f.find(finder_request, response)

    def compute_diff(self, base_version, other_version):
        ''' Compare two versions '''
        assert base_version.time is not None
        assert other_version.time is not None
        try:
            self.security.check_read_block(base_version.block)
        except NotInStoreException:
            raise NotFoundException("Block version %s not found!\n" % str(base_version.block))

        try:
            self.security.check_read_block(other_version.block)
        except NotInStoreException:
            raise NotFoundException("Block version %s not found!\n" % str(other_version.block))

        #This biiService is only for published versions
        return compare_remote_versions(self, base_version, other_version)

    def get_renames(self, brl_block, t1, t2):
        '''Gets 2 BlockVersion ([0],[1]) in a list and returns the renames'''
        security = Security(self._auth_user, self._store)
        security.check_read_block(brl_block)
        block = self._store.read_block(brl_block)
        return block.get_renames(t1, t2)

    def require_auth(self):
        """Only for validating token
        (Used in publish manager to ensure logged user before publishing)"""
        if not self._auth_user:
            raise ForbiddenException()

    def authenticate(self, username, password):
        """ Create a "profile" object (object to encrypt) and expiration time.
        Then return the JWT token Expiration time as a UTC UNIX timestamp
        (an int) or as a datetime"""
        user_service = UserService(self._store, self._auth_user)
        try:
            _, token = user_service.authenticate(username, password)
            return token
        except NotActivatedUser:
            raise BiiRequestErrorException("User account: %s is not confirmed. Check your "
                                           "email account and follow the instructions" % username)
예제 #26
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)
예제 #27
0
class UserService(object):
    """Handles the registration, user profile updating, user confirmation.
    """

    def __init__(self, store, auth_user):
        self.store = store
        self.auth_user = auth_user
        self.security = Security(auth_user, store)

    def edit_user(self, brl_user):
        """Get User fields for edition"""
        self.security.check_update_user(brl_user)
        user = self.get_user(brl_user)
        user = user_to_json(user)
        return user

    def view_user(self, brl_user):
        try:
            user = self.get_user(brl_user)
        except NotInStoreException:
            raise NotFoundException("No user found with name %s" % brl_user)
        # FIXME: Can read email
        user_json = user_to_json(user)
        del user_json["visible_email"]
        del user_json["allow_mailing"]
        if not user.visible_email and brl_user != self.auth_user:
            user_json["email"] = None
        return user_json

    def get_user(self, brl_user):
        '''Retrieve user information'''
        try:
            user = self.store.read_user(brl_user)
        except NotInStoreException:
            raise NotFoundException()
        if not user.active:
            raise NotFoundException()
        # Not propagate sensible information
        user.staff = None
        user.last_api_call = None
        user.active = None
        user.confirmation_token = None
        user.joined_date = None
        user.confirmation_date = None

        auth_blocks = {}
        # Read all blocks and filter private ones
        for brl_block, block_meta in user.blocks.iteritems():
            try:
                block_access = self.store.read_block_permissions(brl_block)
                self.security.check_read_block(brl_block)
                # block_meta => ([tags], description, bytes)
                block_meta.append(block_access.is_private)
                auth_blocks[brl_block] = block_meta
            except ForbiddenException:
                pass
        user.blocks = auth_blocks

        return user

    def register(self, brl_user, email, plain_password, allow_mailing,
                 provider=None, access_token=None, invited_by=None):

        '''
        :param: user is a web_api.model.User
        '''
        # Validate password
        if len(plain_password) < MIN_PASSWORD_LENGTH:
            logger.debug("Invalid password length for %s" % email)
            raise ControledErrorException("Password length must"
                                          " be %s characters min" % MIN_PASSWORD_LENGTH)
        # Search users with same email
        if self.store.read_user_by_email(email):
            logger.debug("Email '%s' already exists!" % email)
            raise ControledErrorException("Email '%s' already exists! Forgot password? "
                                          "Go to login and click on forgot password" % email)

        try:
            brl_user = BRLUser(brl_user)
            bii_user = User(brl_user)
            bii_user.password = plain_password
        except InvalidNameException as e:
            raise ControledErrorException(e)

        # Search invited_by user (by mail or login)
        friend = None
        if invited_by:
            if "@" in invited_by:  # email address
                friend = self.store.read_user_by_email(invited_by)
                friend = friend.ID if friend else None
            else:  # Login
                friend_object = self.store.exists_user_id_ignoring_case(invited_by)
                if friend_object and friend_object.active:
                    friend = invited_by
            if not friend:
                raise ControledErrorException("User %s doesn't exist" % invited_by)
        bii_user.invited_by = friend

        # Check the existing of user name (User.ID), with case-insensitive
        if self.store.exists_user_id_ignoring_case(brl_user):
            logger.debug("User name '%s' already exists!" % brl_user)
            raise ControledErrorException("Username '%s' already exists! "
                                          "Choose other username" % brl_user)

        try:
            bii_user.email = email
            bii_user.allow_mailing = allow_mailing

            manager = JWTConfirmEmailManagerFactory.new()
            token = manager.get_token_for(brl_user)

            bii_user.confirmation_token = token
            bii_user.joined_date = datetime.datetime.now()
            bii_user.active = False

            oauth_service = get_oauth_service(self.store)
            oauth_user_info = oauth_service.get_user_info(provider, access_token)
            self.store.create_user(bii_user)

            if oauth_user_info:
                # If user has changed the oauth email, not confirm the account
                if oauth_user_info[1] == bii_user.email:
                    bii_user.active = True
                try:
                    register_signup(self.store, brl_user)
                except Exception as exc:
                    logger.error("Can't register sign-up in background! %s" % str(exc))

                bii_user.fill_user_oauth_token(provider, access_token)
                self.store.update_user(bii_user)

            return bii_user

        except Exception as e:
            logger.error("Error creating user at mongo: %s" % str(e))
            logger.error(traceback.format_exc())
            raise e

    def confirm_account(self, confirmation_token):
        '''
        Confirms user in database
        '''
        try:
            # Decode token
            jwt_manager = JWTConfirmEmailManagerFactory.new()
            brl_user = jwt_manager.get_confirmed_user(confirmation_token)
            user = self.store.read_user(brl_user)
        except NotInStoreException:
            raise NotFoundException("User '%s' doesn't exist" % brl_user)
        if user.confirmation_token == confirmation_token:
            if not user.active:  # Do not re-send things if already activated
                try:
                    register_signup(self.store, brl_user)
                except Exception as exc:
                    logger.error("Can't register sign-up in background! %s" % str(exc))

            user.active = True
            user.confirmation_date = datetime.datetime.now()
            self.store.update_user(user)
            jwt_auth_manager = JWTCredentialsManagerFactory.new(self.store)
            token = jwt_auth_manager.get_token_for(brl_user)

            return token, brl_user, user.ga_client_id

        else:
            raise NotFoundException("Invalid user or token")

    def confirm_password_reset(self, confirmation_token):
        '''
        Confirms password change. User and password are inside the token
        '''
        try:
            # Decode token
            jwt_manager = JWTPasswordResetManagerFactory.new()
            brl_user, plain_password = jwt_manager.get_user_and_password(confirmation_token)
            user = self.store.read_user(brl_user)
        except Exception:
            raise NotFoundException("No user found with name %s" % brl_user)
        # Update password
        user.password = plain_password
        user.active = True  # If not active, activate now, email is validated
        self.store.update_user(user)

        # Generate an auth token to autologin user
        jwt_auth_manager = JWTCredentialsManagerFactory.new(self.store)
        token = jwt_auth_manager.get_token_for(brl_user)
        return token, brl_user

    def update(self, brl_user, new_fields):
        try:
            self.security.check_update_user(brl_user)
            user = self.store.read_user(brl_user)
            user.firstname = new_fields["firstname"]
            user.lastname = new_fields["lastname"]
            user.country = new_fields["country"]
            user.description = new_fields["description"]
            user.street_1 = new_fields["street_1"]
            user.street_2 = new_fields["street_2"]
            user.city = new_fields["city"]
            user.postal_code = new_fields["postal_code"]
            user.region = new_fields["region"]
            user.tax_id = new_fields["tax_id"]
            user.vat = new_fields["vat"]
            # Tsgs is for internal use yet
            # user.tags = set(new_fields["tags"])
            user.visible_email = new_fields["visible_email"]
            user.allow_mailing = new_fields["allow_mailing"]

            self.store.update_user(user)
        except NotInStoreException:
            raise NotFoundException("No user found with name %s" % brl_user)

    def change_password(self, brl_user, old_password, new_plain_password):
        ''' Changes the password for the specified user'''
        logger.debug("Change password for user %s" % brl_user)
        self.security.check_change_password(brl_user)
        user = self.store.read_user(brl_user)
        if user.valid_password(old_password):
            logger.debug("old password ok")
            try:
                user.password = new_plain_password
            except InvalidNameException as e:
                raise ControledErrorException(e)
            self.store.update_user(user)
            logger.debug("Updated user!")
        else:
            raise ControledErrorException("Invalid password!")

    def authenticate(self, brl_user, password):
        """ Create a "profile" object (object to encrypt) and expiration time.
        Then return the JWT token Expiration time as a UTC UNIX timestamp
        (an int) or as a datetime"""
        try:
            brl_user = BRLUser(brl_user)
        except InvalidNameException:
            raise AuthenticationException("Wrong user or password")
        self._check_password(brl_user, password)
        manager = JWTCredentialsManagerFactory.new(self.store)
        token = manager.get_token_for(brl_user)
        return brl_user, token

    def _check_password(self, nickname, password):
        ''' Check user brl_user/password '''
        try:
            user = self.store.read_user(nickname)
        except Exception:
            raise AuthenticationException("Wrong user or password")
        if user.active:
            if not user.valid_password(password):
                raise AuthenticationException("Wrong user or password")
        else:
            raise NotActivatedUser("User email is not confirmed! "
                                   "We have sent an email to your account")
예제 #28
0
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 __init__(self, store, auth_user):
     self._store = store
     self.security = Security(auth_user, self._store)
예제 #30
0
class ReferenceTranslatorService(object):
    def __init__(self, store, auth_user):
        self._store = store
        self.security = Security(auth_user, self._store)

    def get_published_resources(self, references):
        result = ReferencedResources()
        for block_version, cell_names in references.iteritems():
            try:
                self.security.check_read_block(block_version.block)
                block = self._store.read_block(block_version.block)
                cell_ids = block.cells.get_ids(cell_names, block_version.time)
                content_ids = block.contents.get_ids(cell_names,
                                                     block_version.time)
                cells = self._store.read_published_cells(cell_ids.values())
                contents = self._store.read_published_contents(
                    content_ids.values())
                for name, rID in cell_ids.iteritems():
                    if name in content_ids:
                        cid = content_ids[name]
                        cid = contents[cid]
                    else:
                        cid = None  # Virtual resource
                    result[block_version][name] = Resource(cells[rID], cid)
            except (ForbiddenException, NotInStoreException):
                pass
        return result

    def get_published_min_refs(self, references):
        '''returns the minimum information required to perform a compatibility check for those
        references. This method is currently used just by CompatibilityClosureBuilder

        param references: {block_version: set(cell_names)}
        return: {block_version: {cell_name: (cell_id, content_id), root_id, [deps blockcellnames]}}
        '''
        result = defaultdict(dict)
        for block_version, cell_names in references.iteritems():
            try:
                self.security.check_read_block(block_version.block)
                block = self._store.read_block(block_version.block)
                cell_ids = block.cells.get_ids(cell_names, block_version.time)
                content_ids = block.contents.get_ids(cell_names,
                                                     block_version.time)
                cells = self._store.read_min_cells(cell_ids.values())
                #This cells are {cellID: (rootID, dep_block_names)}
                for cell_name, cell_id in cell_ids.iteritems():
                    content_id = content_ids.get(
                        cell_name)  # None if Virtual resource
                    root_id, deps = cells.get(cell_id, (None, None))
                    if root_id is not None:
                        result[block_version][cell_name] = ((cell_id,
                                                             content_id),
                                                            root_id, deps)
            except (ForbiddenException, NotInStoreException):
                pass

        return result

    def get_dep_table(self, block_version):
        self.security.check_read_block(block_version.block)
        block = self._store.read_block(block_version.block)
        table = block.dep_tables.find(block_version.time)
        return table