def run(): tagValues = SecureTagValueAPI(session.auth.user) objects = SecureObjectAPI(session.auth.user) objectIDs = self._resolveQuery(session, objects, parsedQuery) values = [] if tags is None: # delete all tags user has permissions for result = objects.getTagsByObjects(objectIDs, Operation.DELETE_TAG_VALUE) for objectID, paths in result.iteritems(): for path in paths: values.append((objectID, path)) else: # delete only tags requested by user result = objects.getTagsByObjects(objectIDs) for objectID, paths in result.iteritems(): for path in paths: if tags is None or path in tags: values.append((objectID, path)) if values: try: tagValues.delete(values) except UnknownPathError as error: session.log.exception(error) path = error.paths[0] raise TNonexistentTag(path.encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] category, action = getCategoryAndAction(operation) raise TPathPermissionDenied(category, action, path_)
def setUp(self): super(DatasetImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'user') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user)
def run(): objects = SecureObjectAPI(session.auth.user) try: searchQueries = objects.search(valuesByQuery.keys()) except UnknownPathError as error: session.log.exception(error) unknownPath = error.paths[0] raise TNonexistentTag(unknownPath.encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] if operation == Operation.CREATE_OBJECT: raise TUnauthorized() else: raise TNonexistentTag(path_) # Run queries. try: with session.timer.track('index-search'): result = blockingCallFromThread(reactor, searchQueries.get) except SearchError as error: session.log.exception(error) raise TParseError(query, error.message) # Build a result set from the searches. values = {} for parsedQuery, objectIDs in result.iteritems(): for objectID in objectIDs: for tagAndValue in valuesByQuery[parsedQuery]: value = guessValue(tagAndValue.value) # FIXME: this code sucks, but I rather not having # to modify guessValue to return a list, as that # would break other code. # Hopefully, we'll be able to remove this pretty # soon. if isinstance(value, list): value = [item.decode('utf-8') for item in value] if objectID not in values: values[objectID] = {} values[objectID][tagAndValue.path] = value # Update values. if values: tagValues = SecureTagValueAPI(session.auth.user) try: result = tagValues.set(values) except UnknownPathError as error: session.log.exception(error) path = error.paths[0] raise TNonexistentTag(path.encode('utf-8')) except MalformedPathError as error: # FIXME: Modify MalformedPathError to have a path field. raise TInvalidPath(str(error).encode('utf-8')) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] category, action = getCategoryAndAction(operation) raise TPathPermissionDenied(category, action, path_)
def setUp(self): super(SecureObjectAPIWithSuperuserRoleTest, self).setUp() system = createSystemData() self.superuser = system.users[u'fluiddb'] self.permissions = CachingPermissionAPI(self.superuser) self.objects = SecureObjectAPI(self.superuser) UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')])
def run(): objects = SecureObjectAPI(session.auth.user) objectID = objects.get([about]).get(about) if objectID is None: return [] recentActivity = SecureRecentActivityAPI(session.auth.user) result = recentActivity.getForObjects([objectID]) return self._formatResult(result)
def setUp(self): super(SecureObjectAPIWithUserRoleTest, self).setUp() createSystemData() UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')]) self.user = getUser(u'username') TagAPI(self.user).create([(u'username/tag', u'description')]) self.permissions = CachingPermissionAPI(self.user) self.objects = SecureObjectAPI(self.user)
def setUp(self): super(SecureObjectAPIWithAnonymousRoleTest, self).setUp() system = createSystemData() self.anon = system.users[u'anon'] UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')]) self.user = getUser(u'username') self.tags = TagAPI(self.user) self.tags.create([(u'username/tag', u'description')]) self.permissions = CachingPermissionAPI(self.user) self.objects = SecureObjectAPI(self.anon)
def run(): result = TObjectInfo() objects = SecureObjectAPI(session.auth.user) tagPaths = objects.getTagsByObjects([objectID]) if not tagPaths: result.tagPaths = [] return result tagPaths = tagPaths[objectID] if showAbout and u'fluiddb/about' in tagPaths: tagValues = SecureTagValueAPI(session.auth.user) values = tagValues.get([objectID], [u'fluiddb/about']) about = values[objectID][u'fluiddb/about'].value result.about = about.encode('utf-8') result.tagPaths = tagPaths return result
def run(): try: result = SecureObjectAPI(session.auth.user).create(about) except PermissionDeniedError as error: session.log.exception(error) raise TUnauthorized() return str(result)
def setUp(self): super(SecureObjectAPIWithBrokenCacheTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'user', u'password', u'User', u'*****@*****.**')]) self.user = getUser(u'user') self.objects = SecureObjectAPI(self.user)
def setUp(self): super(DatasetImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'user') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user)
def testCreateIsDenied(self): """ L{SecureObjectAPI.create} raises a L{PermissionDeniedError} if it's invoked by a L{User} with the L{Role.ANONYMOUS}. """ objects = SecureObjectAPI(self.anon) error = self.assertRaises(PermissionDeniedError, objects.create) self.assertEqual(self.anon.username, error.username) self.assertEqual([(None, Operation.CREATE_OBJECT)], error.pathsAndOperations)
def _getObjectData(self, user, objects): """Reformat the data to directly import it into Fluidinfo. @param user: The L{User} to use when preparing data to import. @param objects: The C{list} of objects to convert into the L{TagValueAPI.set} format. @return: A C{dict} with object information that can be used to make a L{TagValueAPI.set} call. """ api = SecureObjectAPI(user) data = {} for objectData in objects: # FIXME We could reduce the number of queries here by making # ObjectAPI.create take a 'skipExistanceCheck' parameter and using # ObjectAPI.get to prefetch object IDs for all existing about # values. objectID = api.create(objectData['about']) data[objectID] = objectData['values'] return data
def _getObjectData(self, user, objects): """Reformat the data to directly import it into Fluidinfo. @param user: The L{User} to use when preparing data to import. @param objects: The C{list} of objects to convert into the L{TagValueAPI.set} format. @return: A C{dict} with object information that can be used to make a L{TagValueAPI.set} call. """ api = SecureObjectAPI(user) data = {} for objectData in objects: # FIXME We could reduce the number of queries here by making # ObjectAPI.create take a 'skipExistanceCheck' parameter and using # ObjectAPI.get to prefetch object IDs for all existing about # values. objectID = api.create(objectData['about']) data[objectID] = objectData['values'] return data
def run(): objects = SecureObjectAPI(session.auth.user) # _resolveQuery is implemented in FacadeTagValueMixin objectIDs = self._resolveQuery(session, objects, parsedQuery) # FIXME: This sucks, but right now if the query returns too many # objects, RecentActivityAPI will blow up. While we fix this, it's # better to return a 400 than a 500. if len(objectIDs) > 2000: raise TBadRequest('The given query returns to many objects.') recentActivity = SecureRecentActivityAPI(session.auth.user) result = recentActivity.getForObjects(objectIDs) return self._formatResult(result)
def run(): tagValues = SecureTagValueAPI(session.auth.user) objects = SecureObjectAPI(session.auth.user) objectIDs = self._resolveQuery(session, objects, parsedQuery) if not objectIDs: return dumps({'results': {'id': {}}}) try: values = tagValues.get(objectIDs, tags) except UnknownPathError as error: # One or more of the requested return Tag's doesn't exist. # We'll filter them out and try again because we don't want to # fail the request just because of a missing tag. filteredTags = set(tags) - set(error.paths) if not filteredTags: return dumps({'results': {'id': {}}}) values = tagValues.get(objectIDs, filteredTags) except PermissionDeniedError as error: session.log.exception(error) path_, operation = error.pathsAndOperations[0] raise TNonexistentTag(path_) valuesByObjectID = {} for objectID, tagPaths in values.iteritems(): for tagPath, tagValue in tagPaths.iteritems(): value = tagValue.value if isinstance(value, dict): size = len(value[u'contents']) mimeType = value[u'mime-type'] value = {u'value-type': mimeType, u'size': size} elif isinstance(value, UUID): value = {'value': str(value)} else: value = {'value': value} value['updated-at'] = tagValue.creationTime.isoformat() value['username'] = tagValue.creator.username objectID = str(objectID) valuesByObjectID.setdefault(objectID, {})[tagPath] = value result = {'results': {'id': valuesByObjectID}} return dumps(result)
def run(): objects = SecureObjectAPI(session.auth.user) # _resolveQuery is implemented in FacadeTagValueMixin objectIDs = self._resolveQuery(session, objects, parsedQuery) if not objectIDs: return [] # FIXME: This sucks, but right now if the query returns too many # objects, RecentActivityAPI will blow up. While we fix this, it's # better to return a 400 than a 500. if len(objectIDs) > 2000: raise TBadRequest('The given query returns to many objects.') values = SecureTagValueAPI(session.auth.user) result = values.get(objectIDs, [u'fluiddb/users/username']) usernames = [result[objectID][u'fluiddb/users/username'].value for objectID in result] recentActivity = SecureRecentActivityAPI(session.auth.user) result = recentActivity.getForUsers(usernames) return self._formatResult(result)
class CommentImporterTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('log', LoggingResource(format='%(message)s')), ('store', DatabaseResource())] def setUp(self): super(CommentImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'fluidinfo.com', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'fluidinfo.com') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user) def testUpload(self): """ Comments are uploaded directly to Fluidinfo. After performing an upload, all the inserted values must be present in Fluidinfo. """ when = datetime.utcnow() floatTime = timegm(when.utctimetuple()) + float(when.strftime('0.%f')) isoTime = when.isoformat() client = CommentImporter(100) client.upload([{ 'about': [u'one', u'two'], 'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe' }]) about = u'fluidinfo.com joe %s' % isoTime result = self.objects.get([about]) objectID = result[about] result = self.values.get([objectID], [ u'fluidinfo.com/info/about', u'fluidinfo.com/info/text', u'fluidinfo.com/info/timestamp', u'fluidinfo.com/info/url', u'fluidinfo.com/info/username', ]) comment = result[objectID] self.assertEqual([u'one', u'two', u'#wonderful'], comment[u'fluidinfo.com/info/about'].value) self.assertEqual(u'Here is my #wonderful comment', comment[u'fluidinfo.com/info/text'].value) self.assertEqual(floatTime, comment[u'fluidinfo.com/info/timestamp'].value) self.assertEqual(u'http://twitter.com/status/9373973', comment[u'fluidinfo.com/info/url'].value) self.assertEqual(u'joe', comment[u'fluidinfo.com/info/username'].value) def testUploadWithoutAboutValues(self): """ When no explicit about values are in the uploaded comment, there must be no about values stored in Fluidinfo. """ when = datetime.utcnow() isoTime = when.isoformat() client = CommentImporter(100) client.upload([{ 'importer': u'fluidinfo.com', 'text': u'Here is my comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe' }]) about = u'fluidinfo.com joe %s' % isoTime result = self.objects.get([about]) objectID = result[about] result = self.values.get([objectID], [u'fluidinfo.com/info/about']) comment = result[objectID] self.assertEqual([], comment[u'fluidinfo.com/info/about'].value) def testUploadMultipleComments(self): """Multiple comments are inserted in batches.""" when = datetime.utcnow() isoTime = when.isoformat() client = CommentImporter(100) client.upload([{ 'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe' }, { 'importer': u'fluidinfo.com', 'text': u'A #crazy comment', 'timestamp': when, 'url': u'http://twitter.com/status/9279479379', 'username': u'mike' }]) about1 = u'fluidinfo.com joe %s' % isoTime about2 = u'fluidinfo.com mike %s' % isoTime result = self.objects.get([about1, about2]) objectID1 = result[about1] objectID2 = result[about2] comments = self.values.get([objectID1, objectID2], [u'fluidinfo.com/info/text']) comment1 = comments[objectID1] self.assertEqual(u'Here is my #wonderful comment', comment1[u'fluidinfo.com/info/text'].value) comment2 = comments[objectID2] self.assertEqual(u'A #crazy comment', comment2[u'fluidinfo.com/info/text'].value) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new comments.\nImported 2/2 new comments.\n' 'Imported 2 comments in ')) def testUploadUsesBatchSize(self): """ Comments are uploaded in batches when possible, depending on the batch size. """ when = datetime.utcnow() client = CommentImporter(1) client.upload([{ 'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe' }, { 'importer': u'fluidinfo.com', 'text': u'A #crazy comment', 'timestamp': when, 'url': u'http://twitter.com/status/9279479379', 'username': u'mike' }]) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new comments.\nImported 1/2 new comments.\n' 'Imported 2/2 new comments.\nImported 2 comments in ')) def testUploadLogsMessage(self): """ Uploads must prefix log output with the passed message. """ when = datetime.utcnow() client = CommentImporter(100) client.upload([{ 'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe' }], 'message-xxx') self.assertTrue(self.log.getvalue().startswith( 'message-xxx: Importing 1 new comments.\n' 'message-xxx: Imported 1/1 new comments.\n' 'message-xxx: Imported 1 comments in '))
class SecureObjectAPIWithAnonymousRoleTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('client', IndexResource()), ('config', ConfigResource()), ('store', DatabaseResource())] def setUp(self): super(SecureObjectAPIWithAnonymousRoleTest, self).setUp() system = createSystemData() self.anon = system.users[u'anon'] UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')]) self.user = getUser(u'username') self.tags = TagAPI(self.user) self.tags.create([(u'username/tag', u'description')]) self.permissions = CachingPermissionAPI(self.user) self.objects = SecureObjectAPI(self.anon) def testCreateIsDenied(self): """ L{SecureObjectAPI.create} raises a L{PermissionDeniedError} if it's invoked by a L{User} with the L{Role.ANONYMOUS}. """ objects = SecureObjectAPI(self.anon) error = self.assertRaises(PermissionDeniedError, objects.create) self.assertEqual(self.anon.username, error.username) self.assertEqual([(None, Operation.CREATE_OBJECT)], error.pathsAndOperations) def testGetTagsByObjectsPathIsAllowed(self): """ L{SecureObjectAPI.getTagsByObjects} will return all the tags for which the anonymous user has C{Operation.READ_TAG_VALUE} permissions. """ objectID = uuid4() values = {objectID: {u'username/tag': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) self.assertEqual({objectID: [u'username/tag']}, self.objects.getTagsByObjects([objectID])) def testGetTagsByObjectsReturnsOnlyAllowedTags(self): """ L{SecureObjectAPI.getTagsByObjects} will return all the tags for which the anonymous user has C{Operation.READ_TAG_VALUE} permissions, but not those for which the user doesn't have. """ self.tags.create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) result = self.objects.getTagsByObjects([objectID]) expected = {objectID: [u'username/tag2']} self.assertEqual(expected, result) def testGetTagsByObjectsReturnsNoneIfDenied(self): """ L{SecureObjectAPI.getTagsByObjects} will return an empty C{dict} if the L{User} does not have C{Operation.READ_TAG_VALUE} permission on none of the L{Tag}s an object has. """ self.tags.create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.CLOSED, [])]) result = self.objects.getTagsByObjects([objectID]) self.assertEqual({}, result) def testGetTagsByObjectsWithCustomPermission(self): """ L{SecureObjectAPI.getTagsByObjects} optionally accepts a permission type to check for instead of L{Operation.READ_TAG_VALUE}). """ TagAPI(self.user).create([(u'username/open', u'An accessible tag'), (u'username/closed', u'A denied tag')]) objectID = uuid4() SecureTagValueAPI(self.user).set({objectID: {u'username/open': 13, u'username/closed': 17}}) self.permissions.set([(u'username/closed', Operation.DELETE_TAG_VALUE, Policy.CLOSED, [])]) result = self.objects.getTagsByObjects( [objectID], permission=Operation.DELETE_TAG_VALUE) # Result is empty because anonymous users are never allowed to delete # values. self.assertEqual({}, result) def testGetTagsForObjectsOnlyReturnsAccessibleTags(self): """ L{SecureObjectAPI.getTagsForObjects} only returns L{Tag.path}s that the user has C{Operation.READ_TAG_VALUE} permissions for. """ TagAPI(self.user).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() SecureTagValueAPI(self.user).set({objectID: {u'username/tag1': 13, u'username/tag2': 17}}) self.permissions.set([(u'username/tag2', Operation.READ_TAG_VALUE, Policy.CLOSED, [])]) self.assertEqual([u'username/tag1'], self.objects.getTagsForObjects([objectID])) @inlineCallbacks def testSearch(self): """ L{SecureObjectAPI.search} resolves the specified L{Query}s if the anonymous user has C{Operation.READ_TAG_VALUE} permissions on the requested L{Tag.path}s. """ objectID = uuid4() index = ObjectIndex(self.client) yield index.update({objectID: {u'username/tag': 42}}) yield self.client.commit() query = parseQuery(u'username/tag = 42') result = self.objects.search([query]) result = yield result.get() self.assertEqual({query: set([objectID])}, result) @inlineCallbacks def testSearchWithoutPermission(self): """ L{SecureObjectAPI.search} raises a L{PermissionDeniedError} if the anonymous user doesn't have C{Operation.READ_TAG_VALUE} permissions on the requested L{Tag.path}s. """ objectID = uuid4() index = ObjectIndex(self.client) yield index.update({objectID: {u'username/tag': 42}}) yield self.client.commit() self.permissions.set([(u'username/tag', Operation.READ_TAG_VALUE, Policy.CLOSED, [])]) query = parseQuery(u'username/tag = 42') error = self.assertRaises(PermissionDeniedError, self.objects.search, [query]) self.assertEqual(u'anon', error.username) self.assertEqual([('username/tag', Operation.READ_TAG_VALUE)], error.pathsAndOperations) @inlineCallbacks def testSearchWithImplicitObjectCreation(self): """ L{SecureObjectAPI.search} doesn't raise a L{PermissionDeniedError} if the anonymous user tries to create new objects using C{fluiddb/about} queries, instead an empty result is returned. """ query = parseQuery(u'fluiddb/about = "TestObject"') result = self.objects.search([query], True) result = yield result.get() self.assertEqual({query: set()}, result)
class DatasetImporterTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('log', LoggingResource(format='%(message)s')), ('store', DatabaseResource())] def setUp(self): super(DatasetImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'user') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user) def testUpload(self): """ Object data is converted into a format compatible with the L{TagValueAPI.set} method before being uploaded directly into Fluidinfo. """ client = DatasetImporter(100) client.upload(u'user', [{'about': u'hello world', 'values': {u'user/bar': 13}}]) result = self.objects.get([u'hello world']) objectID = result[u'hello world'] result = self.values.get([objectID], [u'user/bar']) value = result[objectID][u'user/bar'] self.assertEqual(13, value.value) def testUploadMultipleObjects(self): """Multiple objects are inserted in batches.""" client = DatasetImporter(100) client.upload(u'user', [{'about': u'hello world', 'values': {u'user/bar': 13}}, {'about': u'wubble', 'values': {u'user/quux': 42}}]) aboutValues = self.objects.get([u'hello world', u'wubble']) objectID = aboutValues[u'hello world'] result = self.values.get([objectID], [u'user/bar']) value = result[objectID][u'user/bar'] self.assertEqual(13, value.value) objectID = aboutValues[u'wubble'] result = self.values.get([objectID], [u'user/quux']) value = result[objectID][u'user/quux'] self.assertEqual(42, value.value) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new objects.\nImported 2/2 new objects.\n' 'Imported 2 objects in ')) def testUploadUsesBatchSize(self): """ Objects are uploaded in batches when possible, depending on the batch size. """ client = DatasetImporter(1) client.upload(u'user', [{'about': u'hello world', 'values': {u'user/bar': 13}}, {'about': u'wubble', 'values': {u'user/quux': 42}}]) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new objects.\nImported 1/2 new objects.\n' 'Imported 2/2 new objects.\nImported 2 objects in ')) def testUploadWithUncreatablePath(self): """ L{DatasetImporter.upload} checks permissions when importing data. An L{UnknownPathError} is raised if a specified tag doesn't exist and the L{User} doesn't have permissions to create it. """ client = DatasetImporter(100) self.assertRaises( UnknownPathError, client.upload, u'user', [{'about': u'hello world', 'values': {u'foo/bar': 13}}]) def testUploadWithPermissionViolation(self): """L{DatasetImporter.upload} checks permissions when importing data.""" UserAPI().create([(u'user1', u'pwd', u'User 1', u'*****@*****.**')]) client = DatasetImporter(100) self.assertRaises( PermissionDeniedError, client.upload, u'user', [{'about': u'hello world', 'values': {u'user1/bar': 13}}]) def testUploadWithUnknownUser(self): """ L{DatasetImporter.upload} raises an L{UnknownUserError} if the specified L{User} doesn't exist. """ client = DatasetImporter(100) self.assertRaises( UnknownUserError, client.upload, u'unknown', [{'about': u'hello world', 'values': {u'unknown/bar': 13}}]) def testUploadLogsMessage(self): """ Uploads must prefix log output with the passed message. """ client = DatasetImporter(100) client.upload(u'user', [{'about': u'hello world', 'values': {u'user/bar': 13}}], 'message-xxx') self.assertTrue(self.log.getvalue().startswith( 'message-xxx: Importing 1 new objects.\n' 'message-xxx: Imported 1/1 new objects.\n' 'message-xxx: Imported 1 objects in '))
class SecureObjectAPIWithUserRoleTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('store', DatabaseResource())] def setUp(self): super(SecureObjectAPIWithUserRoleTest, self).setUp() createSystemData() UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')]) self.user = getUser(u'username') TagAPI(self.user).create([(u'username/tag', u'description')]) self.permissions = CachingPermissionAPI(self.user) self.objects = SecureObjectAPI(self.user) def testGetTagsByObjectsPathIsAllowed(self): """ L{SecureObjectAPI.getTagsByObjects} will return all the tags for which the user has C{Operation.READ_TAG_VALUE} permissions. """ objectID = uuid4() values = {objectID: {u'username/tag': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) result = self.objects.getTagsByObjects([objectID]) expected = {objectID: [u'username/tag']} self.assertEqual(expected, result) def testGetTagsByObjectsReturnsOnlyAllowedTags(self): """ L{SecureObjectAPI.getTagsByObjects} will return all the tags for which the user has C{Operation.READ_TAG_VALUE} permissions, but not those for which the user doesn't have. """ TagAPI(self.user).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) result = self.objects.getTagsByObjects([objectID]) expected = {objectID: [u'username/tag2']} self.assertEqual(expected, result) def testGetTagsByObjectsReturnsNoneAllowedTags(self): """ L{SecureObjectAPI.getTagsByObjects} will return all the tags for which the user has C{Operation.READ_TAG_VALUE} permissions, but not those for which the user doesn't have. """ TagAPI(self.user).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.user).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.CLOSED, [])]) result = self.objects.getTagsByObjects([objectID]) self.assertEqual({}, result) def testGetTagsByObjectsWithCustomPermission(self): """ L{SecureObjectAPI.getTagsByObjects} optionally accepts a permission type to check for instead of L{Operation.READ_TAG_VALUE}). """ TagAPI(self.user).create([(u'username/open', u'An accessible tag'), (u'username/closed', u'A denied tag')]) objectID = uuid4() SecureTagValueAPI(self.user).set({objectID: {u'username/open': 13, u'username/closed': 17}}) self.permissions.set([(u'username/closed', Operation.DELETE_TAG_VALUE, Policy.CLOSED, [])]) result = self.objects.getTagsByObjects( [objectID], permission=Operation.DELETE_TAG_VALUE) self.assertEqual({objectID: [u'username/open']}, result) def testGetTagsForObjectsOnlyReturnsAccessibleTags(self): """ L{SecureObjectAPI.getTagsForObjects} only returns L{Tag.path}s that the user has C{Operation.READ_TAG_VALUE} permissions for. """ TagAPI(self.user).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() SecureTagValueAPI(self.user).set({objectID: {u'username/tag1': 13, u'username/tag2': 17}}) self.permissions.set([(u'username/tag2', Operation.READ_TAG_VALUE, Policy.CLOSED, [])]) self.assertEqual([u'username/tag1'], self.objects.getTagsForObjects([objectID]))
def run(): objects = SecureObjectAPI(session.auth.user) objectIDs = self._resolveQuery(session, objects, parsedQuery) return [str(objectID) for objectID in objectIDs]
class DatasetImporterTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('log', LoggingResource(format='%(message)s')), ('store', DatabaseResource())] def setUp(self): super(DatasetImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'user') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user) def testUpload(self): """ Object data is converted into a format compatible with the L{TagValueAPI.set} method before being uploaded directly into Fluidinfo. """ client = DatasetImporter(100) client.upload(u'user', [{ 'about': u'hello world', 'values': { u'user/bar': 13 } }]) result = self.objects.get([u'hello world']) objectID = result[u'hello world'] result = self.values.get([objectID], [u'user/bar']) value = result[objectID][u'user/bar'] self.assertEqual(13, value.value) def testUploadMultipleObjects(self): """Multiple objects are inserted in batches.""" client = DatasetImporter(100) client.upload(u'user', [{ 'about': u'hello world', 'values': { u'user/bar': 13 } }, { 'about': u'wubble', 'values': { u'user/quux': 42 } }]) aboutValues = self.objects.get([u'hello world', u'wubble']) objectID = aboutValues[u'hello world'] result = self.values.get([objectID], [u'user/bar']) value = result[objectID][u'user/bar'] self.assertEqual(13, value.value) objectID = aboutValues[u'wubble'] result = self.values.get([objectID], [u'user/quux']) value = result[objectID][u'user/quux'] self.assertEqual(42, value.value) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new objects.\nImported 2/2 new objects.\n' 'Imported 2 objects in ')) def testUploadUsesBatchSize(self): """ Objects are uploaded in batches when possible, depending on the batch size. """ client = DatasetImporter(1) client.upload(u'user', [{ 'about': u'hello world', 'values': { u'user/bar': 13 } }, { 'about': u'wubble', 'values': { u'user/quux': 42 } }]) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new objects.\nImported 1/2 new objects.\n' 'Imported 2/2 new objects.\nImported 2 objects in ')) def testUploadWithUncreatablePath(self): """ L{DatasetImporter.upload} checks permissions when importing data. An L{UnknownPathError} is raised if a specified tag doesn't exist and the L{User} doesn't have permissions to create it. """ client = DatasetImporter(100) self.assertRaises(UnknownPathError, client.upload, u'user', [{ 'about': u'hello world', 'values': { u'foo/bar': 13 } }]) def testUploadWithPermissionViolation(self): """L{DatasetImporter.upload} checks permissions when importing data.""" UserAPI().create([(u'user1', u'pwd', u'User 1', u'*****@*****.**')]) client = DatasetImporter(100) self.assertRaises(PermissionDeniedError, client.upload, u'user', [{ 'about': u'hello world', 'values': { u'user1/bar': 13 } }]) def testUploadWithUnknownUser(self): """ L{DatasetImporter.upload} raises an L{UnknownUserError} if the specified L{User} doesn't exist. """ client = DatasetImporter(100) self.assertRaises(UnknownUserError, client.upload, u'unknown', [{ 'about': u'hello world', 'values': { u'unknown/bar': 13 } }]) def testUploadLogsMessage(self): """ Uploads must prefix log output with the passed message. """ client = DatasetImporter(100) client.upload(u'user', [{ 'about': u'hello world', 'values': { u'user/bar': 13 } }], 'message-xxx') self.assertTrue(self.log.getvalue().startswith( 'message-xxx: Importing 1 new objects.\n' 'message-xxx: Imported 1/1 new objects.\n' 'message-xxx: Imported 1 objects in '))
class SecureObjectAPIWithSuperuserRoleTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('store', DatabaseResource())] def setUp(self): super(SecureObjectAPIWithSuperuserRoleTest, self).setUp() system = createSystemData() self.superuser = system.users[u'fluiddb'] self.permissions = CachingPermissionAPI(self.superuser) self.objects = SecureObjectAPI(self.superuser) UserAPI().create([(u'username', u'password', u'User', u'*****@*****.**')]) def testGetTagsByObjectsIsAlwaysAllowed(self): """ L{SecureObjectAPI.getTagsByObjects} is always allowed for the superuser. """ TagAPI(self.superuser).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.superuser).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) result = self.objects.getTagsByObjects([objectID]) self.assertEqual(1, len(result)) self.assertIn(objectID, result) self.assertEqual([u'username/tag1', u'username/tag2'], sorted(result[objectID])) def testGetTagsByObjectsWithCustomPermission(self): """ L{SecureObjectAPI.getTagsByObjects} optionally accepts a permission type to check for instead of L{Operation.READ_TAG_VALUE}). """ TagAPI(self.superuser).create( [(u'username/open', u'An accessible tag'), (u'username/closed', u'A denied tag')]) objectID = uuid4() SecureTagValueAPI(self.superuser).set( {objectID: {u'username/open': 13, u'username/closed': 17}}) self.permissions.set([(u'username/closed', Operation.DELETE_TAG_VALUE, Policy.CLOSED, [])]) result = self.objects.getTagsByObjects( [objectID], permission=Operation.DELETE_TAG_VALUE) # Superuser can always delete values, regardless of permission settings result[objectID].sort() self.assertEqual({objectID: [u'username/closed', u'username/open']}, result) def testGetTagsForObjectsIsAlwaysAllowed(self): """ L{SecureObjectAPI.getTagsForObjects} is always allowed for the superuser. """ TagAPI(self.superuser).create([(u'username/tag1', u'description'), (u'username/tag2', u'description')]) objectID = uuid4() values = {objectID: {u'username/tag1': 16, u'username/tag2': 16}} SecureTagValueAPI(self.superuser).set(values) self.permissions.set([(u'username/tag1', Operation.READ_TAG_VALUE, Policy.CLOSED, []), (u'username/tag2', Operation.READ_TAG_VALUE, Policy.OPEN, [])]) self.assertEqual( sorted([u'username/tag1', u'username/tag2']), sorted(self.objects.getTagsForObjects([objectID])))
class CommentImporterTest(FluidinfoTestCase): resources = [('cache', CacheResource()), ('config', ConfigResource()), ('log', LoggingResource(format='%(message)s')), ('store', DatabaseResource())] def setUp(self): super(CommentImporterTest, self).setUp() self.system = createSystemData() UserAPI().create([ (u'fluidinfo.com', u'secret', u'User', u'*****@*****.**')]) user = getUser(u'fluidinfo.com') self.objects = SecureObjectAPI(user) self.values = SecureTagValueAPI(user) def testUpload(self): """ Comments are uploaded directly to Fluidinfo. After performing an upload, all the inserted values must be present in Fluidinfo. """ when = datetime.utcnow() floatTime = timegm(when.utctimetuple()) + float(when.strftime('0.%f')) isoTime = when.isoformat() client = CommentImporter(100) client.upload([ {'about': [u'one', u'two'], 'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe'} ]) about = u'fluidinfo.com joe %s' % isoTime result = self.objects.get([about]) objectID = result[about] result = self.values.get([objectID], [u'fluidinfo.com/info/about', u'fluidinfo.com/info/text', u'fluidinfo.com/info/timestamp', u'fluidinfo.com/info/url', u'fluidinfo.com/info/username', ]) comment = result[objectID] self.assertEqual([u'one', u'two', u'#wonderful'], comment[u'fluidinfo.com/info/about'].value) self.assertEqual(u'Here is my #wonderful comment', comment[u'fluidinfo.com/info/text'].value) self.assertEqual(floatTime, comment[u'fluidinfo.com/info/timestamp'].value) self.assertEqual(u'http://twitter.com/status/9373973', comment[u'fluidinfo.com/info/url'].value) self.assertEqual(u'joe', comment[u'fluidinfo.com/info/username'].value) def testUploadWithoutAboutValues(self): """ When no explicit about values are in the uploaded comment, there must be no about values stored in Fluidinfo. """ when = datetime.utcnow() isoTime = when.isoformat() client = CommentImporter(100) client.upload([ {'importer': u'fluidinfo.com', 'text': u'Here is my comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe'} ]) about = u'fluidinfo.com joe %s' % isoTime result = self.objects.get([about]) objectID = result[about] result = self.values.get([objectID], [u'fluidinfo.com/info/about']) comment = result[objectID] self.assertEqual([], comment[u'fluidinfo.com/info/about'].value) def testUploadMultipleComments(self): """Multiple comments are inserted in batches.""" when = datetime.utcnow() isoTime = when.isoformat() client = CommentImporter(100) client.upload([ {'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe'}, {'importer': u'fluidinfo.com', 'text': u'A #crazy comment', 'timestamp': when, 'url': u'http://twitter.com/status/9279479379', 'username': u'mike'} ]) about1 = u'fluidinfo.com joe %s' % isoTime about2 = u'fluidinfo.com mike %s' % isoTime result = self.objects.get([about1, about2]) objectID1 = result[about1] objectID2 = result[about2] comments = self.values.get([objectID1, objectID2], [u'fluidinfo.com/info/text']) comment1 = comments[objectID1] self.assertEqual(u'Here is my #wonderful comment', comment1[u'fluidinfo.com/info/text'].value) comment2 = comments[objectID2] self.assertEqual(u'A #crazy comment', comment2[u'fluidinfo.com/info/text'].value) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new comments.\nImported 2/2 new comments.\n' 'Imported 2 comments in ')) def testUploadUsesBatchSize(self): """ Comments are uploaded in batches when possible, depending on the batch size. """ when = datetime.utcnow() client = CommentImporter(1) client.upload([ {'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe'}, {'importer': u'fluidinfo.com', 'text': u'A #crazy comment', 'timestamp': when, 'url': u'http://twitter.com/status/9279479379', 'username': u'mike'} ]) self.assertTrue(self.log.getvalue().startswith( 'Importing 2 new comments.\nImported 1/2 new comments.\n' 'Imported 2/2 new comments.\nImported 2 comments in ')) def testUploadLogsMessage(self): """ Uploads must prefix log output with the passed message. """ when = datetime.utcnow() client = CommentImporter(100) client.upload([ {'importer': u'fluidinfo.com', 'text': u'Here is my #wonderful comment', 'timestamp': when, 'url': u'http://twitter.com/status/9373973', 'username': u'joe'}], 'message-xxx') self.assertTrue(self.log.getvalue().startswith( 'message-xxx: Importing 1 new comments.\n' 'message-xxx: Imported 1/1 new comments.\n' 'message-xxx: Imported 1 comments in '))