Ejemplo n.º 1
0
class FluidinfoSessionFactoryTest(FluidinfoTestCase):

    resources = [('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FluidinfoSessionFactoryTest, self).setUp()
        self.transact = Transact(self.threadPool)

    def testCreate(self):
        """L{SessionFactory.create} creates a new L{Session} instance."""
        now = datetime.utcnow()
        factory = FluidinfoSessionFactory('API-9001', lambda: now)
        session = factory.create(self.transact)
        expectedDate = now.strftime('%Y%m%d-%H%M%S')
        self.assertEqual('API-9001-%s-000001' % expectedDate, session.id)

    def testCreateIncrementsCount(self):
        """
        The counter, appended at the end of the L{Session.id}, is incremented
        each time an L{Session} is created.
        """
        now = datetime.utcnow()
        factory = FluidinfoSessionFactory('API-9001', lambda: now)
        factory.create(self.transact)
        session = factory.create(self.transact)
        expectedDate = now.strftime('%Y%m%d-%H%M%S')
        self.assertEqual('API-9001-%s-000002' % expectedDate, session.id)
Ejemplo n.º 2
0
class FacadeTest(FluidinfoTestCase):
    """
    Simple tests of L{Facade} functionality that are not to do with user
    creation or authentication.
    """

    resources = [('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.system = createSystemData()

    @inlineCallbacks
    def testCreateAnonymousSession(self):
        """
        L{FacadeAuthMixin.createAnonymousSession} creates a
        L{FluidinfoSession} for the anonymous user C{anon} so that anonymous
        requests coming from the C{WSFE} can be correctly verified by the
        L{Facade}.
        """
        anon = self.system.users[u'anon']
        self.store.commit()

        session = yield self.facade.createAnonymousSession()
        self.assertEqual('anon', session.auth.username)
        self.assertEqual(anon.objectID, session.auth.objectID)
Ejemplo n.º 3
0
class RootResourceTest(FluidinfoTestCase):

    resources = [('config', ConfigResource()), ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(RootResourceTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        transact = Transact(self.threadPool)
        createSystemData()
        self.checker = AnonymousChecker()
        self.checker.facadeClient = Facade(transact, factory)
        getConfig().set('service', 'allow-anonymous-access', 'False')

    @inlineCallbacks
    def testAnonymousAccessDenied(self):
        """
        L{FacadeAnonymousCheckerTest.requestAvatarId} returns
        an C{UnauthorizedLogin} for access by the C{anon} user if the
        C{allow-anonymous-access} configuration option is C{False}. The
        C{UnauthorizedLogin} is the C{session} attribute in L{RootResource} and
        must result in the C{getChild} method returning a
        L{WSFEUnauthorizedResource} instance.
        """
        self.store.commit()
        session = yield self.checker.requestAvatarId(credentials=None)
        self.assertTrue(isinstance(session, UnauthorizedLogin))
        root = RootResource(self.checker.facadeClient, session)
        request = FakeRequest()
        root.getChild('/', request)
Ejemplo n.º 4
0
class FacadeCheckerTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeCheckerTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        transact = Transact(self.threadPool)
        createSystemData()
        self.checker = FacadeChecker()
        self.checker.facadeClient = Facade(transact, factory)

    def testRequestAvatarIdWithIncorrectPassword(self):
        """
        L{FacadeChecker.requestAvatarId} when passed credentials with an
        incorrect password must raise C{UnauthorizedLogin}.
        """
        createUser(u'user', u'pass', u'User', u'*****@*****.**')
        self.store.commit()
        credentials = UsernamePassword('user', 'bad password')
        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)

    def testRequestAvatarIdWithNonExistentUser(self):
        """
        L{FacadeChecker.requestAvatarId} when passed credentials with a
        non-existent user must raise C{UnauthorizedLogin}.
        """
        credentials = UsernamePassword('user', 'pass')
        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)

    @inlineCallbacks
    def testRequestAvatarId(self):
        """
        L{FacadeChecker.requestAvatarId} when passed credentials creates a
        L{FluidinfoSession} for the authenticated user only if credentials
        are correct.
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        self.store.commit()
        credentials = UsernamePassword('user', 'pass')
        session = yield self.checker.requestAvatarId(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)
Ejemplo n.º 5
0
class TimerPluginTest(FluidinfoTestCase):

    resources = [('log', LoggingResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(TimerPluginTest, self).setUp()
        self.transact = Transact(self.threadPool)

    def testTrack(self):
        """
        L{TimerPlugin.track} returns a context manager that records the amount
        of time a block of code takes to run.
        """
        session = SampleSession('id', self.transact)
        session.start()
        try:
            with session.timer.track('test'):
                pass
        finally:
            session.stop()

        self.assertIn('test', session.timer.events)
        [info] = session.timer.events['test']
        self.assertEqual(3, len(info))
        self.assertTrue(isinstance(info['startDate'], datetime))
        self.assertTrue(isinstance(info['stopDate'], datetime))
        self.assertTrue(isinstance(info['duration'], timedelta))

    def testDumpsAndLoads(self):
        """
        Data stored by a L{TimerPlugin} can be dumped to and loaded from JSON.
        """
        session = SampleSession('id', self.transact)
        session.start()
        try:
            with session.timer.track('test'):
                pass
        finally:
            session.stop()

        data = session.dumps()
        loadedSession = SampleSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(session.timer.events, loadedSession.timer.events)
Ejemplo n.º 6
0
class FacadeAnonymousCheckerTest(FluidinfoTestCase):

    resources = [('config', ConfigResource()), ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeAnonymousCheckerTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        transact = Transact(self.threadPool)
        createSystemData()
        self.checker = AnonymousChecker()
        self.checker.facadeClient = Facade(transact, factory)

    @inlineCallbacks
    def testRequestAvatarIdWithAnonymousAccessDenied(self):
        """
        L{FacadeAnonymousCheckerTest.requestAvatarId} returns
        C{UnauthorizedLogin} for the C{anon} user if the
        C{allow-anonymous-access} configuration option is C{False}.
        """
        getConfig().set('service', 'allow-anonymous-access', 'False')
        self.store.commit()
        session = yield self.checker.requestAvatarId(credentials=None)
        self.assertTrue(isinstance(session, UnauthorizedLogin))

    @inlineCallbacks
    def testRequestAvatarId(self):
        """
        L{FacadeAnonymousCheckerTest.requestAvatarId} creates a
        L{FluidinfoSession} for the anonymous 'anon' user if the
        C{allow-anonymous-access} configuration option is C{True}.
        """
        getConfig().set('service', 'allow-anonymous-access', 'True')
        self.store.commit()
        session = yield self.checker.requestAvatarId(credentials=None)
        self.assertEqual('anon', session.auth.username)
Ejemplo n.º 7
0
class ConcreteUserResourceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('client', IndexResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(ConcreteUserResourceTest, self).setUp()
        createSystemData()
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.store.commit()

    @defer.inlineCallbacks
    def testGET(self):
        """
        A GET request on /users/<username> returns the complete details about
        the user.
        """
        request = FakeRequest()
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = ConcreteUserResource(self.facade, session, 'username')
            body = yield resource.deferred_render_GET(request)
            body = loads(body)
            expected = {"role": "USER",
                        "name": "User",
                        "id": str(self.user.objectID)}
            self.assertEqual(expected, body)
            self.assertEqual(http.OK, request.code)

    @defer.inlineCallbacks
    def testPUT(self):
        """
        A PUT request on C{/users/<username>} updates the data for the user.
        """
        body = dumps({'name': 'New name',
                      'email': '*****@*****.**',
                      'role': 'USER_MANAGER'})

        headers = Headers({'content-length': [len(body)],
                           'content-type': ['application/json']})

        request = FakeRequest(body=body, headers=headers)
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = ConcreteUserResource(self.facade, session, 'username')
            yield resource.deferred_render_PUT(request)
            self.assertEqual(http.NO_CONTENT, request.code)
            body = yield resource.deferred_render_GET(FakeRequest())
            body = loads(body)
            expected = {'role': 'USER_MANAGER',
                        'name': 'New name',
                        'id': str(self.user.objectID)}
            self.assertEqual(expected, body)

    @defer.inlineCallbacks
    def testPUTToUpdateRoleOnly(self):
        """
        A PUT request on C{/users/<username>} can update only the role for the
        user even if other arguments are not given.
        """
        body = dumps({'role': 'USER_MANAGER'})

        headers = Headers({'content-length': [len(body)],
                           'content-type': ['application/json']})

        request = FakeRequest(body=body, headers=headers)
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = ConcreteUserResource(self.facade, session, 'username')
            yield resource.deferred_render_PUT(request)
            self.assertEqual(http.NO_CONTENT, request.code)
            body = yield resource.deferred_render_GET(FakeRequest())
            body = loads(body)
            expected = {'role': 'USER_MANAGER',
                        'name': 'User',
                        'id': str(self.user.objectID)}
            self.assertEqual(expected, body)
Ejemplo n.º 8
0
class VerifyUserPasswordResourceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(VerifyUserPasswordResourceTest, self).setUp()
        self.transact = Transact(self.threadPool)
        createSystemData()
        UserAPI().create([
            (u'fluidinfo.com', 'secret', u'Fluidinfo', u'*****@*****.**'),
            (u'user', u'pass', u'Peter Parker', u'*****@*****.**')])
        consumer = getUser(u'anon')
        OAuthConsumerAPI().register(consumer)
        self.store.commit()

    @defer.inlineCallbacks
    def testPostWithCorrectPasswordReturnsCorrectKeys(self):
        """
        A C{POST} to C{/users/user/verify} with the correct password returns a
        JSON object with all the expected keys, including valid = True.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.OK)
            result = loads(request.response)
            self.assertEqual(
                ['accessToken', 'fullname', 'renewalToken', 'role', 'valid'],
                sorted(result.keys()))
            self.assertTrue(result['valid'])

    @defer.inlineCallbacks
    def testPostWithCorrectPasswordDoesNotCauseALogWarning(self):
        """
        A C{POST} to C{/users/user/verify} with the correct password should
        not cause a complaint about unknown return payload fields in the
        logging system.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            logOutput = self.log.getvalue()
            self.assertNotIn("unknown response payload field 'renewalToken'",
                             logOutput)
            self.assertNotIn("unknown response payload field 'accessToken'",
                             logOutput)

    @defer.inlineCallbacks
    def testPostWithCorrectPasswordReturnsCorrectRole(self):
        """
        A C{POST} to C{/users/user/verify} with the correct password returns a
        JSON object with the correct user role.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.OK)
            result = loads(request.response)
            self.assertEqual('USER', result['role'])

    @defer.inlineCallbacks
    def testPostWithCorrectPasswordReturnsCorrectFullname(self):
        """
        A C{POST} to C{/users/user/verify} with the correct password returns a
        JSON object with the user's correct full name.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.OK)
            result = loads(request.response)
            self.assertEqual(u'Peter Parker', result['fullname'])

    @defer.inlineCallbacks
    def testPostWithIncorrectPasswordReturnsFalse(self):
        """
        A C{POST} to C{/users/user/verify} with the incorrect password returns
        a C{{'valid': False}} response.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.OK)
            self.assertEqual(loads(request.response), {'valid': False})

    @defer.inlineCallbacks
    def testPostWithUnknownUsernameReturnsNotFound(self):
        """
        A C{POST} to C{/users/user/verify} with an unknown username returns a
        404 Not Found.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'unknown')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.NOT_FOUND)

    @defer.inlineCallbacks
    def testPostWithoutPasswordReturnsBadRequest(self):
        """
        A C{POST} to C{/users/user/verify} without a password returns a 400
        Bad Request.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = ''
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.BAD_REQUEST)

    @defer.inlineCallbacks
    def testPostWithExtraCrapInPayloadReturnsBadRequest(self):
        """
        A C{POST} to C{/users/user/verify} with unexpected data in the payload
        returns a 400 Bad Request.
        """
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******', 'foo': 'bar'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json'],
                       'X-Forwarded-Protocol': ['https']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.BAD_REQUEST)

    @defer.inlineCallbacks
    def testInsecurePostIsRejected(self):
        """A C{POST} via HTTP is rejected if not in development mode."""
        self.config.set('service', 'development', 'false')
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = ''
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.BAD_REQUEST)
            self.assertEqual(
                request.getResponseHeader('X-FluidDB-Message'),
                '/users/<username>/verify requests must use HTTPS')

    @defer.inlineCallbacks
    def testInsecurePostIsNotRejectedInDevelopmentMode(self):
        """A C{POST} via HTTP is not rejected when in development mode."""
        self.config.set('service', 'development', 'true')
        with login(None, None, self.transact) as session:
            resource = VerifyUserPasswordResource(None, session, 'user')
            payload = dumps({'password': '******'})
            headers = {'Content-Length': [str(len(payload))],
                       'Content-Type': ['application/json']}
            request = FakeRequest(method='POST', headers=Headers(headers),
                                  body=payload)
            self.assertEqual(NOT_DONE_YET, resource.render(request))

            yield resource.deferred
            self.assertEqual(request.code, http.OK)
Ejemplo n.º 9
0
class FacadeObjectMixinTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeObjectMixinTest, self).setUp()
        self.system = createSystemData()
        self.transact = Transact(self.threadPool)
        factory = FluidinfoSessionFactory('API-9000')
        self.facade = Facade(self.transact, factory)
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        self.permissions = CachingPermissionAPI(self.user)

    @inlineCallbacks
    def testCreateObjectPermissionDenied(self):
        """
        L{FacadeObjectMixin.createObject} raises a
        L{TUnauthorized} exception if the user is the anonymous user.
        """
        self.store.commit()

        with login(u'anon', uuid4(), self.transact) as session:
            deferred = self.facade.createObject(session)
            yield self.assertFailure(deferred, TUnauthorized)

    @inlineCallbacks
    def testCreateObjectWithoutAboutValue(self):
        """
        L{FacadeObjectAPI.createObject} always returns a valid C{UUID} in a
        C{str} for a new object ID if an about value is not given.
        """
        self.store.commit()

        with login(u'username', uuid4(), self.transact) as session:
            objectID = yield self.facade.createObject(session)
            self.assertEqual(objectID, str(UUID(objectID)))

    @inlineCallbacks
    def testCreateObjectWithAboutValue(self):
        """
        L{FacadeObjectAPI.createObject} always returns a valid C{UUID} in a
        C{str} for a new object ID if an about value is given and it doesn't
        already exist.
        """
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            objectID = yield self.facade.createObject(session, about='foo')
            objectID = UUID(objectID)

        self.store.rollback()
        value = getAboutTagValues([objectID], [u'foo']).one()
        self.assertEqual(u'foo', value.value)

        tag = self.system.tags[u'fluiddb/about']
        value = getTagValues(values=[(objectID, tag.id)]).one()
        self.assertIdentical(self.system.users[u'fluiddb'], value.creator)

    @inlineCallbacks
    def testCreateObjectWithDuplicateAboutValue(self):
        """
        L{FacadeObjectAPI.createObject} returns a valid C{UUID} in a C{str} for
        an existing object ID if an about value is given and already exists in
        the database.
        """
        objectID = uuid4()
        createAboutTagValue(objectID, u'bar')
        values = {objectID: {u'fluiddb/about': u'bar'}}
        SecureTagValueAPI(self.system.superuser).set(values)
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            resultObjectID = yield self.facade.createObject(session,
                                                            about='bar')
            self.assertEqual(str(objectID), resultObjectID)

    @inlineCallbacks
    def testGetObject(self):
        """
        L{FacadeObjectAPI.getObject} returns a L{TObjectInfo} with the
        L{Tag.path}s for which the L{User} has L{Operation.READ_TAG_VALUE}.
        """
        objectID = uuid4()
        SecureTagAPI(self.user).create([(u'username/foo', u'A description')])
        values = {objectID: {u'username/foo': u'bar'}}
        SecureTagValueAPI(self.user).set(values)
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            objectInfo = yield self.facade.getObject(session, str(objectID))
            self.assertEqual([u'username/foo'], objectInfo.tagPaths)
            self.assertEqual(None, objectInfo.about)

    @inlineCallbacks
    def testGetObjectWithAboutValue(self):
        """
        L{FacadeObjectAPI.getObject} returns a L{TObjectInfo} with the
        L{Tag.path}s for which the L{User} has L{Operation.READ_TAG_VALUE},
        and the L{AboutTagValue.value} if it has one and C{showAbout} is
        C{True}.
        """
        objectID = uuid4()
        SecureTagAPI(self.user).create([(u'username/foo', u'A description')])
        values = {objectID: {u'username/foo': u'bar'}}
        SecureTagValueAPI(self.user).set(values)
        aboutTag = self.system.tags[u'fluiddb/about']
        createAboutTagValue(objectID, u'bar')
        createTagValue(self.user.id, aboutTag.id, objectID, u'about value')
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            objectInfo = yield self.facade.getObject(session,
                                                     str(objectID),
                                                     showAbout=True)
            self.assertEqual([u'fluiddb/about', u'username/foo'],
                             sorted(objectInfo.tagPaths))
            self.assertEqual('about value', objectInfo.about)

    @inlineCallbacks
    def testGetObjectWithAboutValueDoesNotExist(self):
        """
        L{FacadeObjectAPI.getObject} returns a L{TObjectInfo} with the
        L{Tag.path}s for which the L{User} has L{Operation.READ_TAG_VALUE},
        and the L{AboutTagValue.value} if it has one and C{showAbout} is
        C{True}.
        """
        objectID = uuid4()
        tag = createTag(self.user, self.user.namespace, u'foo')
        createTagPermission(tag)
        createTagValue(self.user.id, tag.id, objectID, u'bar')
        session = AuthenticatedSession(self.user.username, uuid4())
        self.store.commit()
        with login(self.user.username, uuid4(), self.transact) as session:
            objectInfo = yield self.facade.getObject(session,
                                                     str(objectID),
                                                     showAbout=True)
            self.assertEqual([u'username/foo'], objectInfo.tagPaths)
            self.assertIdentical(None, objectInfo.about)

    @inlineCallbacks
    def testGetObjectDeniedPaths(self):
        """
        L{FacadeObjectAPI.getObject} returns a L{TObjectInfo} with the
        L{Tag.path}s for which the L{User} has L{Operation.READ_TAG_VALUE}.
        """
        SecureTagAPI(self.user).create([(u'username/tag1', u'description'),
                                        (u'username/tag2', u'description')])
        objectID = uuid4()
        values = {
            objectID: {
                u'username/tag1': 16
            },
            objectID: {
                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, [])
        ])
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            objectInfo = yield self.facade.getObject(session, str(objectID))
            self.assertEqual([u'username/tag2'], objectInfo.tagPaths)
            self.assertEqual(None, objectInfo.about)

    @inlineCallbacks
    def testGetObjectAllPathsAreDenied(self):
        """
        L{FacadeObjectAPI.getObject} returns a L{TObjectInfo} with the
        L{Tag.path}s for which the L{User} has L{Operation.READ_TAG_VALUE}, if
        all of them are denied, L{FacadeObjectAPI.getObject} must return an
        empty L{TObjectInfo}.
        """
        SecureTagAPI(self.user).create([(u'username/tag1', u'description'),
                                        (u'username/tag2', u'description')])
        objectID = uuid4()
        values = {
            objectID: {
                u'username/tag1': 16
            },
            objectID: {
                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, [])
        ])
        self.store.commit()

        with login(self.user.username, uuid4(), self.transact) as session:
            objectInfo = yield self.facade.getObject(session, str(objectID))
            self.assertEqual([], objectInfo.tagPaths)
            self.assertEqual(None, objectInfo.about)
Ejemplo n.º 10
0
class RenewOAuthTokenResourceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(RenewOAuthTokenResourceTest, self).setUp()
        self.transact = Transact(self.threadPool)
        createSystemData()

    @inlineCallbacks
    def testRenderWithSuccessfulVerification(self):
        """
        An C{OK} HTTP status code is returned, along with new access and
        renewal tokens, if a renewal request is successful.
        """
        UserAPI().create(
            [(u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
             (u'user', 'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'consumer')
        api = OAuthConsumerAPI()
        api.register(consumer)
        token = api.getRenewalToken(consumer, user)
        self.store.commit()

        headers = {'X-FluidDB-Renewal-Token': [token.encrypt()]}
        request = FakeRequest(headers=Headers(headers))
        with login(u'consumer', consumer.objectID, self.transact) as session:
            resource = RenewOAuthTokenResource(session)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))

            yield resource.deferred
            headers = dict(request.responseHeaders.getAllRawHeaders())
            self.assertTrue(headers['X-Fluiddb-Access-Token'])
            self.assertTrue(headers['X-Fluiddb-Renewal-Token'])
            self.assertEqual(OK, request.code)

    @inlineCallbacks
    def testRenderWithUnknownConsumer(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if an
        L{OAuthRenewalToken} for an unknown L{OAuthConsumer} is used in a
        renewal request.
        """
        UserAPI().create(
            [(u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
             (u'user', 'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'consumer')
        api = OAuthConsumerAPI()
        api.register(consumer)
        token = api.getRenewalToken(consumer, user)
        headers = {'X-FluidDB-Renewal-Token': [token.encrypt()]}
        self.store.find(OAuthConsumer).remove()
        self.store.commit()

        request = FakeRequest(headers=Headers(headers))
        with login(u'consumer', consumer.objectID, self.transact) as session:
            resource = RenewOAuthTokenResource(session)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))
            yield resource.deferred
            headers = dict(request.responseHeaders.getAllRawHeaders())
            self.assertEqual(
                {'X-Fluiddb-Error-Class': ['UnknownConsumer'],
                 'X-Fluiddb-Username': ['consumer'],
                 'X-Fluiddb-Request-Id': [session.id]},
                headers)
            self.assertEqual(BAD_REQUEST, request.code)

    @inlineCallbacks
    def testRenderWithExpiredRenewalToken(self):
        """
        An C{UNAUTHORIZED} HTTP status code is returned if an expired
        L{OAuthRenewalToken} is used in a renewal request.
        """
        UserAPI().create(
            [(u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
             (u'user', 'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'consumer')
        api = OAuthConsumerAPI()
        api.register(consumer)
        creationTime = datetime.utcnow() - timedelta(hours=200)
        token = api.getRenewalToken(consumer, user, now=lambda: creationTime)
        self.store.commit()

        headers = {'X-FluidDB-Renewal-Token': [token.encrypt()]}
        request = FakeRequest(headers=Headers(headers))
        with login(u'consumer', consumer.objectID, self.transact) as session:
            resource = RenewOAuthTokenResource(session)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))
            yield resource.deferred
            headers = dict(request.responseHeaders.getAllRawHeaders())
            self.assertEqual(
                {'X-Fluiddb-Error-Class': ['ExpiredOAuth2RenewalToken'],
                 'X-Fluiddb-Username': ['consumer'],
                 'X-Fluiddb-Request-Id': [session.id]},
                headers)
            self.assertEqual(UNAUTHORIZED, request.code)

    @inlineCallbacks
    def testRenderWithInvalidRenewalToken(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if an invalid
        L{OAuthRenewalToken} is used in a renewal request.
        """
        UserAPI().create(
            [(u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
             (u'user', 'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        api = OAuthConsumerAPI()
        api.register(consumer)
        self.store.commit()

        headers = {'X-FluidDB-Renewal-Token': ['invalid']}
        request = FakeRequest(headers=Headers(headers))
        with login(u'consumer', consumer.objectID, self.transact) as session:
            resource = RenewOAuthTokenResource(session)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))
            yield resource.deferred
            headers = dict(request.responseHeaders.getAllRawHeaders())
            self.assertEqual(
                {'X-Fluiddb-Error-Class': ['InvalidOAuth2RenewalToken'],
                 'X-Fluiddb-Username': ['consumer'],
                 'X-Fluiddb-Request-Id': [session.id]},
                headers)
            self.assertEqual(BAD_REQUEST, request.code)
Ejemplo n.º 11
0
class SecureCommentAPIWithUserRoleTest(CommentAPITestMixin, FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(SecureCommentAPIWithUserRoleTest, self).setUp()
        createSystemData()
        UserAPI().create([
            (u'username', u'password', u'User', u'*****@*****.**'),
            (u'fluidinfo.com', u'secret', u'Fluidinfo', u'*****@*****.**')])
        self.user = getUser(u'username')
        self.comments = SecureCommentAPI(self.user)

    def testCreateSucceeds(self):
        """
        L{SecureCommentAPI.create} returns a C{dict} with the expected
        keys and values.
        """
        when = datetime.utcnow()
        floatTime = timegm(when.utctimetuple()) + float(when.strftime('0.%f'))
        isoTime = when.isoformat()
        result = self.comments.create(u'Comment text', u'username', when=when)
        expected = {
            'fluidinfo.com/info/about': [],
            'fluidinfo.com/info/text': u'Comment text',
            'fluidinfo.com/info/timestamp': floatTime,
            'fluidinfo.com/info/url': (
                'https://fluidinfo.com/comment/fluidinfo.com/username/' +
                isoTime),
            'fluidinfo.com/info/username': u'username',
        }
        self.assertEqual(expected, result)

    def testDeleteAnotherUsersComment(self):
        """
        L{SecureObjectAPI.delete} raises a L{PermissionDeniedError} if a
        L{User} tries to delete a comment made by someone else.
        """
        error = self.assertRaises(PermissionDeniedError, self.comments.delete,
                                  'digg.com', 'joe', datetime.utcnow())
        self.assertEqual(u'username', error.username)
        self.assertEqual(
            [(u'fluidinfo.com/info/username', Operation.DELETE_TAG_VALUE)],
            error.pathsAndOperations)

    def testUpdateAnotherUsersComment(self):
        """
        L{SecureObjectAPI.update} raises a L{PermissionDeniedError} if a
        L{User} tries to update a comment made by someone else.
        """
        error = self.assertRaises(PermissionDeniedError, self.comments.update,
                                  'digg.com', 'joe', datetime.utcnow(), u'new')
        self.assertEqual(u'username', error.username)
        self.assertEqual(
            [(u'fluidinfo.com/info/username', Operation.WRITE_TAG_VALUE)],
            error.pathsAndOperations)

    def testGetForObjectWithAdditionalTagsUnreadable(self):
        """
        L{SecureCommentAPI.getForObject} raises a L{PermissionDeniedError} if a
        L{User} tries to retrieve C{additionalTags} which are unreadable to
        them.
        """
        objectID = uuid4()
        values = {objectID: {u'username/tag': 16}}
        SecureTagValueAPI(self.user).set(values)
        CachingPermissionAPI(self.user).set([(u'username/tag',
                                              Operation.READ_TAG_VALUE,
                                              Policy.CLOSED, [])])

        error = self.assertRaises(PermissionDeniedError,
                                  self.comments.getForObject, u'about',
                                  additionalTags=[u'username/tag'])
        self.assertEqual(u'username', error.username)
        self.assertEqual(
            [(u'username/tag', Operation.READ_TAG_VALUE)],
            error.pathsAndOperations)

    def testGetForObjectWithAdditionalTagsNonexistent(self):
        """
        L{SecureCommentAPI.getForObject} raises a L{PermissionDeniedError} if a
        L{User} tries to retrieve C{additionalTags} which are unreadable to
        them.
        """
        self.assertRaises(UnknownPathError,
                          self.comments.getForObject, u'about',
                          additionalTags=[u'user/nonexistent'])

    def testGetForUserWithAdditionalTagsUnreadable(self):
        """
        L{SecureCommentAPI.getForUser} raises a L{PermissionDeniedError} if a
        L{User} tries to retrieve C{additionalTags} which are unreadable to
        them.
        """
        objectID = uuid4()
        values = {objectID: {u'username/tag': 16}}
        SecureTagValueAPI(self.user).set(values)
        CachingPermissionAPI(self.user).set([(u'username/tag',
                                              Operation.READ_TAG_VALUE,
                                              Policy.CLOSED, [])])

        error = self.assertRaises(PermissionDeniedError,
                                  self.comments.getForUser, u'username',
                                  additionalTags=[u'username/tag'])
        self.assertEqual(u'username', error.username)
        self.assertEqual(
            [(u'username/tag', Operation.READ_TAG_VALUE)],
            error.pathsAndOperations)

    def testGetForUserWithAdditionalTagsNonexistent(self):
        """
        L{SecureCommentAPI.getForUser} raises a L{PermissionDeniedError} if a
        L{User} tries to retrieve C{additionalTags} which are unreadable to
        them.
        """
        self.assertRaises(UnknownPathError,
                          self.comments.getForUser, u'username',
                          additionalTags=[u'user/nonexistent'])
Ejemplo n.º 12
0
class RecentObjectsActivityResourceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('client', IndexResource()), ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(RecentObjectsActivityResourceTest, self).setUp()
        createSystemData()
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)

    @inlineCallbacks
    def testRenderRecentObjectsActivity(self):
        """
        L{RecentObjectsActivityResource.deferred_render_GET} renders a response
        with recent activity data for the objects returned by the given query.
        """
        objectID = ObjectAPI(self.user).create(u'object1')
        self.store.commit()
        TagValueAPI(self.user).set({objectID: {u'username/tag1': u'A'}})
        runDataImportHandler(self.client.url)
        request = FakeRequest(args={'query': ['has username/tag1']})
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = RecentObjectsActivityResource(self.facade, session)
            body = yield resource.deferred_render_GET(request)
            body = json.loads(body)
            expected = [{
                u'username': u'username',
                u'about': u'object1',
                u'id': str(objectID),
                u'tag': u'username/tag1',
                u'value': u'A'
            }, {
                u'username': u'fluiddb',
                u'about': u'object1',
                u'id': str(objectID),
                u'tag': u'fluiddb/about',
                u'value': u'object1'
            }]
            # Clean up timestamps.
            for item in body:
                del item['updated-at']
            self.assertEqual(expected, body)
            self.assertEqual(http.OK, request.code)

    @inlineCallbacks
    def testRenderRecentObjectsActivityWithNoQuery(self):
        """
        L{RecentObjectsActivityResource.deferred_render_GET} raises
        L{MissingArgument} if the C{query} argument is not given.
        """
        objectID = ObjectAPI(self.user).create(u'object1')
        self.store.commit()
        TagValueAPI(self.user).set({objectID: {u'username/tag1': u'A'}})
        runDataImportHandler(self.client.url)
        request = FakeRequest()
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = RecentObjectsActivityResource(self.facade, session)
            deferred = resource.deferred_render_GET(request)
            yield self.assertFailure(deferred, MissingArgument)

    def testGetChildForObjects(self):
        """
        L{RecentObjectsActivityResource.getChild} returns a
        L{RecentObjectActivityResource} to handle C{recent/objects/id}
        requests.
        """
        resource = RecentObjectsActivityResource(None, None)
        objectID = '751ba46f-2f27-4ec3-9271-ff032bd60240'
        request = FakeRequest(postpath=[objectID])
        leafResource = resource.getChild('objects', request)
        self.assertIsInstance(leafResource, RecentObjectActivityResource)

    def testGetChildForItself(self):
        """
        L{RecentObjetsActivityResource.getChild} returns itself for requests
        without name.
        """
        resource = RecentObjectsActivityResource(None, None)
        request = FakeRequest(postpath=[])
        leafResource = resource.getChild('', request)
        self.assertIsInstance(leafResource, RecentObjectsActivityResource)
Ejemplo n.º 13
0
class FacadeAuthenticateUserWithOAuth2Test(FluidinfoTestCase):
    """Test L{Facade} OAuth2 authentication."""

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeAuthenticateUserWithOAuth2Test, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.system = createSystemData()

    @inlineCallbacks
    def testAuthenticateUserWithOAuth2(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        api.register(consumer)
        token = api.getAccessToken(consumer, user)
        self.store.commit()

        credentials = OAuth2Credentials(u'consumer', u'secret',
                                        token.encrypt())
        session = yield self.facade.authenticateUserWithOAuth2(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateUserWithOAuth2IgnoresCase(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        api.register(consumer)
        token = api.getAccessToken(consumer, user)
        self.store.commit()

        credentials = OAuth2Credentials(u'ConsumeR', u'secret',
                                        token.encrypt())
        session = yield self.facade.authenticateUserWithOAuth2(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateUserWithOAuthWithMixedCaseToken(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} ignores case in the
        username in the token.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        oauthConsumer = api.register(consumer)
        token = dataToToken(oauthConsumer.secret,
                            {'username': u'UseR',
                             'creationTime': '20121228-161823'})
        self.store.commit()

        credentials = OAuth2Credentials(consumer.username, u'secret', token)
        session = yield self.facade.authenticateUserWithOAuth2(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateAnonymousUserWithOAuth2(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} should create a
        L{FluidinfoSession} for the anonymous user.
        """
        anonymous = self.system.users[u'anon']
        UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')])
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        api.register(anonymous)
        token = api.getAccessToken(anonymous, user)
        self.store.commit()

        credentials = OAuth2Credentials(u'anon', None, token.encrypt())
        session = yield self.facade.authenticateUserWithOAuth2(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    def testAuthenticateUserWithOAuth2ConsumerPasswordIncorrect(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} raises
        L{TPasswordIncorrect} if the consumer's password is not correct.
        """
        user1 = createUser(u'user1', u'pass1', u'User1', u'*****@*****.**')
        oauthConsumer1 = createOAuthConsumer(user1, secret='secret16charlng1')
        user2 = createUser(u'user2', u'pass2', u'User2', u'*****@*****.**')
        self.store.commit()

        token = dataToToken(oauthConsumer1.secret,
                            {'username': user2.username})

        credentials = OAuth2Credentials(u'user1', u'invalid', token)
        deferred = self.facade.authenticateUserWithOAuth2(credentials)

        return self.assertFailure(deferred, TPasswordIncorrect)

    def testAuthenticateUserWithOAuth2UnknownConsumer(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} raises
        L{TNoSuchUser} if the consumer does not exist.
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        oauthConsumer = createOAuthConsumer(user, secret='secret16charlng1')
        self.store.commit()

        token = dataToToken(oauthConsumer.secret, {'username': u'unknownUser'})
        credentials = OAuth2Credentials(u'invalid', u'pass', token)
        deferred = self.facade.authenticateUserWithOAuth2(credentials)

        return self.assertFailure(deferred, TNoSuchUser)

    def testAuthenticateUserWithOAuth2UnregisteredConsumer(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} raises
        L{TPasswordIncorrect} if the consumer exists as a Fluidinfo user
        but is not registered as an OAuth consumer.
        """
        createUser(u'user1', u'pass1', u'User1', u'*****@*****.**')
        createUser(u'user2', u'pass2', u'User2', u'*****@*****.**')
        self.store.commit()

        token = dataToToken('a' * 16, {'username': u'user2'})
        credentials = OAuth2Credentials(u'user1', u'pass1', token)
        deferred = self.facade.authenticateUserWithOAuth2(credentials)

        return self.assertFailure(deferred, TPasswordIncorrect)

    def testAuthenticateUserWithOAuth2UnknownUsernameInToken(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth2} ignores the case in the
        consumer key.
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        oauthConsumer = createOAuthConsumer(user, secret='secret16charlng1')
        self.store.commit()

        token = dataToToken(oauthConsumer.secret, {'username': u'unknownUser'})
        credentials = OAuth2Credentials(u'user', u'pass', token)
        deferred = self.facade.authenticateUserWithOAuth2(credentials)

        return self.assertFailure(deferred, TNoSuchUser)
Ejemplo n.º 14
0
class FacadeOAuthCheckerTest(FluidinfoTestCase):

    resources = [('config', ConfigResource()), ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeOAuthCheckerTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        transact = Transact(self.threadPool)
        createSystemData()
        self.checker = FacadeOAuthChecker()
        self.checker.facadeClient = Facade(transact, factory)

    @inlineCallbacks
    def testRequestAvatarId(self):
        """
        L{FacadeOAuthChecker.requestAvatarId} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        UserAPI().create([(u'consumer', u'secret', u'Consumer',
                           u'*****@*****.**'),
                          (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        consumer = api.register(consumerUser)
        token = api.getAccessToken(consumerUser, user)
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        # FIXME This isn't ideal.  It'd be better to use a hard-coded
        # signature, because then we'd know when something changed.  It's hard
        # to do that, though, because the encrypted token generated by
        # fluiddb.util.minitoken is always different. -jkakar
        request = Request.from_request('GET', u'https://fluidinfo.com/foo',
                                       headers, {'argument1': 'bar'})
        signature = SignatureMethod_HMAC_SHA1().sign(request, consumer, None)
        nonce = 'nonce'
        credentials = OAuthCredentials('fluidinfo.com', consumerUser.username,
                                       token.encrypt(), 'HMAC-SHA1', signature,
                                       timestamp, nonce, 'GET',
                                       u'https://fluidinfo.com/foo', headers,
                                       arguments)
        session = yield self.checker.requestAvatarId(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    def testRequestAvatarIdWithTokenMadeFromWrongSecret(self):
        """
        L{FacadeOAuthChecker.requestAvatarId} creates a L{FluidinfoSession}
        for the authenticated user only if the access token was created
        using the consumer's secret.
        """
        secret = ''.join(sample(ALPHABET, 16))
        user = createUser(u'username', u'password', u'User',
                          u'*****@*****.**')
        createOAuthConsumer(user, secret=secret)
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        token = dataToToken('a' * 16, {'username': user.username})
        signature = 'wrong'
        nonce = 'nonce'

        credentials = OAuthCredentials('fluidinfo.com', user.username, token,
                                       'HMAC-SHA1', signature, timestamp,
                                       nonce, 'GET',
                                       u'https://fluidinfo.com/foo', headers,
                                       arguments)

        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)

    def testRequestAvatarIdInvalidToken(self):
        """
        L{FacadeOAuthChecker.requestAvatarId} creates a
        L{FluidinfoSession} for the authenticated user only if
        the access token was properly formed (by calling dataToToken).
        """
        secret = ''.join(sample(ALPHABET, 16))
        user = createUser(u'username', u'password', u'User',
                          u'*****@*****.**')
        createOAuthConsumer(user, secret=secret)
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        token = 'token'
        signature = 'wrong'
        nonce = 'nonce'

        credentials = OAuthCredentials('fluidinfo.com', user.username, token,
                                       'HMAC-SHA1', signature, timestamp,
                                       nonce, 'GET',
                                       u'https://fluidinfo.com/foo', headers,
                                       arguments)

        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)
Ejemplo n.º 15
0
class HTTPPluginTest(FluidinfoTestCase):

    resources = [('log', LoggingResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(HTTPPluginTest, self).setUp()
        self.transact = Transact(self.threadPool)

    def testTrace(self):
        """
        L{HTTPPlugin.trace} extracts and stores information from a request
        instance.
        """
        headers = Headers({'x-hello': ['goodbye']})
        request = FakeRequest(uri='/objects',
                              method='PUT',
                              path='/objects?foo=bar',
                              headers=headers)
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.http.trace(request)
            request.setHeader('foo', 'bar')
            request.setResponseCode(FORBIDDEN)
        finally:
            session.stop()

        self.assertEqual('/objects', session.http.uri)
        self.assertEqual('/objects?foo=bar', session.http.path)
        self.assertEqual('PUT', session.http.method)
        self.assertEqual(headers, session.http.requestHeaders)
        self.assertEqual({'Foo': ['bar']},
                         dict(session.http.responseHeaders.getAllRawHeaders()))
        self.assertEqual(FORBIDDEN, session.http.code)

    def testSanitizeAuthorizationHeader(self):
        """
        The contents of the C{Authorization} header, when it's present, are
        sanitized to avoid leaking password data.
        """
        headers = Headers({'authorization': ['Basic 123456abcdef']})
        request = FakeRequest(uri='/objects',
                              method='PUT',
                              path='/objects?foo=bar',
                              headers=headers)
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.http.trace(request)
        finally:
            session.stop()

        data = session.dumps()
        loadedSession = SampleSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(Headers({'Authorization': ['<sanitized>']}),
                         loadedSession.http.requestHeaders)

    def testDumpsAndLoads(self):
        """
        Data stored by an L{HTTPPlugin} can be dumped to and loaded from JSON.
        """
        headers = Headers({'x-foo': ['bar']})
        request = FakeRequest(uri='/objects',
                              method='PUT',
                              path='/objects?foo=bar',
                              headers=headers)
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.http.trace(request)
            request.setHeader('foo', 'bar')
            request.setResponseCode(FORBIDDEN)
        finally:
            session.stop()

        data = session.dumps()
        loadedSession = SampleSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(loadedSession.http.uri, session.http.uri)
        self.assertEqual(loadedSession.http.path, session.http.path)
        self.assertEqual(loadedSession.http.method, session.http.method)
        self.assertEqual(loadedSession.http.requestHeaders,
                         session.http.requestHeaders)
        self.assertEqual(loadedSession.http.responseHeaders,
                         session.http.responseHeaders)
        self.assertEqual(loadedSession.http.code, session.http.code)
Ejemplo n.º 16
0
class FacadeAuthenticateUserWithPasswordTest(FluidinfoTestCase):
    """Test L{Facade} user+password authentication."""

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeAuthenticateUserWithPasswordTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.system = createSystemData()

    @inlineCallbacks
    def testAuthenticateUserWithPassword(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        self.store.commit()

        session = yield self.facade.authenticateUserWithPassword('user',
                                                                 'pass')

        self.assertEqual('user', session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateUserWithPasswordIgnoresCase(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} ignores case for the
        the username.
        """
        UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')])
        self.store.commit()

        session = yield self.facade.authenticateUserWithPassword('UsEr',
                                                                 'secret')

        self.assertEqual('user', session.auth.username)

    @inlineCallbacks
    def testAuthenticateUserWithPasswordUnicodeUsername(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} will accept UTF-8
        encoded C{str}s for the username and the password, and convert them to
        C{unicode} appropiately.
        """
        username = u'\N{HIRAGANA LETTER A}'
        password = u'\N{HIRAGANA LETTER E}'
        fullname = u'\N{HIRAGANA LETTER I}'
        email = u'*****@*****.**'
        user = createUser(username, password, fullname, email)
        self.store.commit()

        session = yield self.facade.authenticateUserWithPassword(
            username.encode('utf-8'), password.encode('utf-8'))
        self.assertEqual(username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    def testAuthenticateUserWithPasswordIncorrectPassword(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} raises a
        L{TPasswordIncorrect} exception if the provided password doesn't match
        the L{User}'s.
        """
        createUser(u'username', u'password', u'User', u'*****@*****.**')
        self.store.commit()

        deferred = self.facade.authenticateUserWithPassword('username',
                                                            'bad-password')
        return self.assertFailure(deferred, TPasswordIncorrect)

    def testAuthenticateUserWithPasswordUnknownUser(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} raises a
        L{TNoSuchUser} exception if the provided username doesn't exist in the
        database.
        """
        self.store.commit()
        deferred = self.facade.authenticateUserWithPassword('unknown', 'pwd')
        return self.assertFailure(deferred, TNoSuchUser)

    @inlineCallbacks
    def testAuthenticateUserWithPasswordStopsSessionOnError(self):
        """
        L{FacadeAuthMixin.authenticateUserWithPassword} stops the session if an
        authentication error occurs, preventing memory leaks.
        """
        self.store.commit()

        # We monkeypatch the Session.close method to know if it was called.
        oldStop = Session.stop

        def stopWrapper(*args, **kwargs):
            self.sessionStopped = True
            return oldStop(*args, **kwargs)
        Session.stop = stopWrapper

        self.sessionStopped = False
        deferred = self.facade.authenticateUserWithPassword('unknown', 'pwd')
        yield self.assertFailure(deferred, TNoSuchUser)
        self.assertTrue(self.sessionStopped)

        self.sessionStopped = False
        deferred = self.facade.authenticateUserWithPassword('username', 'bad')
        yield self.assertFailure(deferred, TNoSuchUser)
        self.assertTrue(self.sessionStopped)

        Session.stop = oldStop
Ejemplo n.º 17
0
class TransactPluginTest(FluidinfoTestCase):

    resources = [('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(TransactPluginTest, self).setUp()
        self.transact = Transact(self.threadPool, sleep=lambda seconds: None)

    @inlineCallbacks
    def testRunWithEmptyTransaction(self):
        """Nothing is logged if no statements are run."""
        session = SampleSession('id', self.transact)
        session.start()
        try:
            yield session.transact.run(lambda: None)
        finally:
            session.stop()

        transaction = session.transact.transactions[0]

        self.assertEqual([], transaction['statements'])
        self.assertTrue(isinstance(transaction['duration'], timedelta))
        self.assertTrue(isinstance(transaction['statementDuration'],
                                   timedelta))

    @inlineCallbacks
    def testRun(self):
        """Statements run in a transaction are captured."""
        def run():
            store = getMainStore()
            store.execute('SELECT 1 FROM patch WHERE 1=2')

        session = SampleSession('id', self.transact)
        session.start()
        try:
            yield session.transact.run(run)
        finally:
            session.stop()

        [transaction] = session.transact.transactions
        [trace] = transaction['statements']
        self.assertEqual('SELECT 1 FROM patch WHERE 1=2', trace['statement'])
        self.assertEqual([], trace['parameters'])
        self.assertIn('startDate', trace)
        self.assertIn('stopDate', trace)
        self.assertIn('duration', trace)

    @inlineCallbacks
    def testRunTwoTransactions(self):
        """Statements run in two or more transactions are captured."""
        def run():
            store = getMainStore()
            store.execute('SELECT 1 FROM patch WHERE 1=2')

        def run2():
            store = getMainStore()
            store.execute('SELECT 2 FROM patch WHERE 1=2')

        session = SampleSession('id', self.transact)
        session.start()
        try:
            yield session.transact.run(run)
            yield session.transact.run(run2)
        finally:
            session.stop()

        [transaction1, transaction2] = session.transact.transactions
        [trace] = transaction1['statements']
        self.assertEqual('SELECT 1 FROM patch WHERE 1=2', trace['statement'])
        self.assertEqual([], trace['parameters'])
        self.assertIn('startDate', trace)
        self.assertIn('stopDate', trace)
        self.assertIn('duration', trace)
        [trace] = transaction2['statements']
        self.assertEqual('SELECT 2 FROM patch WHERE 1=2', trace['statement'])
        self.assertEqual([], trace['parameters'])
        self.assertIn('startDate', trace)
        self.assertIn('stopDate', trace)
        self.assertIn('duration', trace)

    @inlineCallbacks
    def testDumpsAndLoads(self):
        """
        Data stored by the L{TransactPlugin} can be dumped to and loaded from
        JSON.
        """
        def run():
            store = getMainStore()
            store.execute('SELECT 1 FROM patch WHERE 1=2')

        session = SampleSession('id', self.transact)
        session.start()
        try:
            yield session.transact.run(run)
        finally:
            session.stop()

        data = session.dumps()
        loadedSession = SampleSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(session.transact.transactions,
                         loadedSession.transact.transactions)
        self.assertEqual(session.transact.totalStatementDuration,
                         loadedSession.transact.totalStatementDuration)

    def testDumpsWithError(self):
        """
        L{TransactPlugin} converts exceptions, such as C{QueryCanceledError}
        that's raised when a statement timeout occurs, to strings when
        generating data that will be serialized to JSON.
        """
        def run():
            store = getMainStore()
            store.execute(u'SELECT * FROM \N{HIRAGANA LETTER A}')

        session = SampleSession('id', self.transact)
        session.transact.timeout = -1
        session.start()

        def check(error):
            session.stop()
            data = session.dumps()
            loadedSession = SampleSession('another-id', self.transact)
            loadedSession.loads(data)
            self.assertEqual(session.transact.transactions,
                             loadedSession.transact.transactions)
            self.assertEqual(session.transact.totalStatementDuration,
                             loadedSession.transact.totalStatementDuration)

        deferred = self.assertFailure(session.transact.run(run),
                                      ProgrammingError)
        return deferred.addCallback(check)

    def testStatementWithNoDurationLogsWarning(self):
        """
        Calling stop on a C{TransactPlugin} that has a statement
        with no 'duration' key should log an error.

        NOTE: This test exists because we currently have a bug elsewhere
        that creates a session statement with no 'duration' key.
        """
        session = SampleSession('id', self.transact)
        session.start()
        transaction = {
            'startDate': datetime.utcnow(),
            'stopDate': datetime.utcnow(),
            'statements': [{}]
        }
        session._plugins['transact'].transactions.append(transaction)
        session.stop()
        logMessages = self.log.getvalue()
        self.assertIn('ERROR', logMessages)
        self.assertIn('Session statement {} has no duration key', logMessages)
Ejemplo n.º 18
0
class FacadeNamespaceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeNamespaceTest, self).setUp()
        createSystemData()
        self.transact = Transact(self.threadPool)
        factory = FluidinfoSessionFactory('API-9000')
        self.facade = Facade(self.transact, factory)
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        self.permissions = PermissionAPI(self.user)

    @inlineCallbacks
    def testGetNamespaceWithoutData(self):
        """
        L{Facade.getNamespace} raises a L{TNonexistentNamespace} exception if
        the requested L{Namespace.path} doesn't exist.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.getNamespace(session, u'username/unknown',
                                                False, False, False)
            yield self.assertFailure(deferred, TNonexistentNamespace)

    @inlineCallbacks
    def testGetNamespace(self):
        """
        L{Facade.getNamespace} returns a L{TNamespace} instance with
        information about the requested L{Namespace}.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            result = yield self.facade.getNamespace(session, u'username',
                                                    False, False, False)

        self.assertEqual(str(self.user.namespace.objectID), result.objectId)
        self.assertEqual(u'username', result.path)

    @inlineCallbacks
    def testGetNamespaceWithDescription(self):
        """
        L{Facade.getNamespace} includes the L{Namespace.description}, if it
        was requested.
        """
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/name', u'A namespace.')])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            result = yield self.facade.getNamespace(session, u'username/name',
                                                    True, False, False)
        self.assertEqual(u'A namespace.', result.description)

    @inlineCallbacks
    def testGetNamespaceWithNamespaces(self):
        """
        L{Facade.getNamespace} includes child L{Namespace.name}s, if they were
        requested.
        """
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/name', u'A namespace.')])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            result = yield self.facade.getNamespace(session, u'username',
                                                    False, True, False)
        self.assertEqual([u'name', u'private'], sorted(result.namespaces))

    @inlineCallbacks
    def testGetNamespaceWithTags(self):
        """
        L{Facade.getNamespace} includes child L{Tag.name}s, if they were
        requested.
        """
        createTag(self.user, self.user.namespace, u'tag')
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            result = yield self.facade.getNamespace(session, u'username',
                                                    False, False, True)
        self.assertEqual([u'tag'], result.tags)

    @inlineCallbacks
    def testGetNamespaceWithUnknownPath(self):
        """
        L{Facade.getNamespace} raises a L{TNonexistentNamespace} exception
        if the specified L{Namespace} doesn't exist.
        """
        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.getNamespace(session,
                                                u'unknown',
                                                returnDescription=False,
                                                returnNamespaces=False,
                                                returnTags=False)
            yield self.assertFailure(deferred, TNonexistentNamespace)

    @inlineCallbacks
    def testGetNamespaceWithPermissionDenied(self):
        """
        L{Facade.getNamespace} raises a L{TPathPermissionDenied} exception
        if the user doesn't have C{LIST} permissions on the specified
        L{Namespace}.
        """
        self.permissions.set([(u'username', Operation.LIST_NAMESPACE,
                               Policy.CLOSED, [])])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.getNamespace(session,
                                                u'username',
                                                returnDescription=True,
                                                returnNamespaces=True,
                                                returnTags=True)
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testCreateNamespace(self):
        """L{Facade.createNamespace} creates a new L{Namespace}."""
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            objectID = yield self.facade.createNamespace(
                session, u'username', u'name', u'A namespace.')

        self.store.rollback()
        self.assertNotIdentical(None, objectID)
        objectID = UUID(objectID)
        namespace = getNamespaces(objectIDs=[objectID]).one()
        self.assertIdentical(self.user, namespace.creator)
        self.assertEqual(u'username/name', namespace.path)
        self.assertEqual(u'name', namespace.name)
        self.assertEqual(objectID, namespace.objectID)

    @inlineCallbacks
    def testCreateNamespaceWithExistingPath(self):
        """
        L{Facade.createNamespace} raises a L{TNamespaceAlreadyExists}
        exception if the new L{Namespace} already exists.
        """
        createNamespace(self.user, u'username/name', self.user.namespace.id)
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.createNamespace(session, u'username',
                                                   u'name', u'A namespace.')
            yield self.assertFailure(deferred, TNamespaceAlreadyExists)

    @inlineCallbacks
    def testCreateNamespaceWithUnknownParent(self):
        """
        L{Facade.createNamespace} raises a L{TNonexistentNamespace} exception
        if a non-existent parent L{Namespace} is specified.
        """
        createNamespace(self.user, u'username/name', self.user.namespace.id)
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.createNamespace(session, u'unknown/parent',
                                                   u'name', u'A  namespace.')
            yield self.assertFailure(deferred, TNonexistentNamespace)

    @inlineCallbacks
    def testCreateNamespaceWithInvalidPath(self):
        """
        L{Facade.createNamespace} raises a L{TInvalidPath} exception
        if the path of the L{Namespace} is not well formed.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.createNamespace(session, u'username',
                                                   u'bad name', u'description')
            yield self.assertFailure(deferred, TInvalidPath)

    @inlineCallbacks
    def testCreateNamespaceImplicitlyCreatesParent(self):
        """
        L{Facade.createNamespace} implicitly creates parent L{Namespace}s if
        they don't exist.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            objectID = yield self.facade.createNamespace(
                session, u'username/parent', u'name', u'A namespace.')
            namespace = yield self.facade.getNamespace(session,
                                                       u'username/parent/name',
                                                       returnDescription=False,
                                                       returnNamespaces=False,
                                                       returnTags=False)
            self.assertEqual(objectID, namespace.objectId)

    @inlineCallbacks
    def testCreateIsDenied(self):
        """
        L{Facade.createNamespace} raises a L{TPathPermissionDenied} exception
        if the user doesn't have C{CREATE} permissions on the parent
        L{Namespace}.
        """
        self.permissions.set([(u'username', Operation.CREATE_NAMESPACE,
                               Policy.CLOSED, [])])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.createNamespace(session, u'username',
                                                   u'test', u'description')
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testUpdateNamespace(self):
        """
        L{Facade.updateNamespace} updates the description for an existing
        L{Namespace}.
        """
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/name', u'A namespace.')])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            yield self.facade.updateNamespace(session, u'username/name',
                                              u'A new description.')

        self.store.rollback()
        result = namespaces.get([u'username/name'], withDescriptions=True)
        self.assertEqual(u'A new description.',
                         result[u'username/name']['description'])

    @inlineCallbacks
    def testUpdateNamespaceWithUnknownPath(self):
        """
        L{Facade.updateNamespace} raises a L{TNonexistentNamespace} exception
        if the requested L{Namespace.path} doesn't exist.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.updateNamespace(session,
                                                   u'username/unknown',
                                                   u'A new description.')
            yield self.assertFailure(deferred, TNonexistentNamespace)

    @inlineCallbacks
    def testUpdateIsDenied(self):
        """
        L{Facade.updateNamespace} raises a L{TPathPermissionDenied} exception
        if the user doesn't have C{UPDATE} permissions on the specified
        L{Namespace}.
        """
        self.permissions.set([(u'username', Operation.UPDATE_NAMESPACE,
                               Policy.CLOSED, [])])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.updateNamespace(session, u'username',
                                                   u'description')
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testDeleteNamespace(self):
        """L{Facade.deleteNamespace} deletes a L{Namespace}."""
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/name', u'A namespace.')])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            yield self.facade.deleteNamespace(session, u'username/name')

        self.store.rollback()
        self.assertEqual({}, namespaces.get([u'username/name']))

    @inlineCallbacks
    def testDeleteNamespaceWithUnknownPath(self):
        """
        L{Facade.deleteNamespace} raises a L{TNonexistentNamespace} exception
        if the requested L{Namespace.path} doesn't exist.
        """
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.deleteNamespace(session,
                                                   u'username/unknown')
            yield self.assertFailure(deferred, TNonexistentNamespace)

    @inlineCallbacks
    def testDeleteNamespaceWithData(self):
        """
        L{Facade.deleteNamespace} raises a L{TNamespaceNotEmpty} exception if
        the requested L{Namespace} has child data such as other L{Namespace}s
        or L{Tag}s.
        """
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/parent', u'A parent namespace.')])
        namespaces.create([(u'username/parent/child', u'A child namespace.')])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.deleteNamespace(session, u'username/parent')
            yield self.assertFailure(deferred, TNamespaceNotEmpty)

    @inlineCallbacks
    def testDeleteIsDenied(self):
        """
        L{Facade.deleteNamespace} raises a L{TPathPermissionDenied} exception
        if the user doesn't have C{DELETE} permissions on the specified
        L{Namespace}.
        """
        namespaces = NamespaceAPI(self.user)
        namespaces.create([(u'username/test', u'description')])
        self.permissions.set([(u'username/test', Operation.DELETE_NAMESPACE,
                               Policy.OPEN, [u'username'])])
        self.store.commit()

        with login(u'username', self.user.objectID, self.transact) as session:
            deferred = self.facade.deleteNamespace(session, u'username/test')
            yield self.assertFailure(deferred, TPathPermissionDenied)
Ejemplo n.º 19
0
class FacadeUserCreationTest(FluidinfoTestCase):
    """Test L{Facade} user creation."""

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeUserCreationTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.system = createSystemData()

    @inlineCallbacks
    def testCreateUserWithPassword(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will create a new user in
        the database and return its object ID when invoked by the superuser.
        """
        superuser = self.system.users[u'fluiddb']
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            objectID = yield self.facade.createUserWithPassword(
                session, 'user', 'secret', 'User', '*****@*****.**')
        self.store.rollback()

        self.assertEqual({u'user': {'id': UUID(objectID), 'name': u'User',
                                    'role': Role.USER}},
                         UserAPI().get([u'user']))

    @inlineCallbacks
    def testCreateUserWithPasswordIgnoresCase(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} ignores the case of the
        username.
        """
        superuser = self.system.users[u'fluiddb']
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            objectID = yield self.facade.createUserWithPassword(
                session, 'UsEr', 'secret', 'User', '*****@*****.**')
        self.store.rollback()

        self.assertEqual({u'user': {'id': UUID(objectID), 'name': u'User',
                                    'role': Role.USER}},
                         UserAPI().get([u'user']))

    @inlineCallbacks
    def testCreateUserUnicode(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will accept UTF-8 encoded
        C{str}s for the username, the password, the full name and the email
        address and convert them to C{unicode} appropriately.
        """
        superuser = self.system.users[u'fluiddb']
        username = u'\N{HIRAGANA LETTER A}'
        password = u'\N{HIRAGANA LETTER E}'
        name = u'\N{HIRAGANA LETTER I}'
        email = u'*****@*****.**'
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            objectID = yield self.facade.createUserWithPassword(
                session, username.encode('utf-8'), password.encode('utf-8'),
                name.encode('utf-8'), email.encode('utf-8'))
        self.store.rollback()
        user = getUser(username)

        self.assertEqual(str(user.objectID), objectID)
        self.assertEqual(user.fullname, name)
        self.assertEqual(user.email, email)

    @inlineCallbacks
    def testCreateUserDuplicate(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will raise a
        L{TUserAlreadyExists} if the username given already exists in the
        database.
        """
        createUser(u'fred', u'password', u'Fred', u'*****@*****.**')
        superuser = self.system.users[u'fluiddb']
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            deferred = self.facade.createUserWithPassword(
                session, 'fred', 'password', 'Fred', '*****@*****.**')
            yield self.assertFailure(deferred, TUserAlreadyExists)

    @inlineCallbacks
    def testCreateUserWithInvalidUsername(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will raise a
        L{TInvalidUsername} exception if the username given is invalid.
        """
        superuser = self.system.users[u'fluiddb']
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            deferred = self.facade.createUserWithPassword(
                session, '!invalid & ', 'secret', 'None', '*****@*****.**')
            yield self.assertFailure(deferred, TInvalidUsername)

    @inlineCallbacks
    def testCreateUserWithLongUsername(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will raise a
        L{TUsernameTooLong} exception if the username given is longer than 128
        characters.
        """
        superuser = self.system.users[u'fluiddb']
        self.store.commit()

        with login(u'fluiddb', superuser.objectID, self.transact) as session:
            session.auth.login(u'fluiddb', superuser.objectID)
            deferred = self.facade.createUserWithPassword(
                session, 'x' * 129, 'secret', 'None', '*****@*****.**')
            yield self.assertFailure(deferred, TUsernameTooLong)

    @inlineCallbacks
    def testCreateUserNormalUserRole(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will raise a
        L{TPathPermissionDenied} if invoked by a normal user.
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        self.store.commit()

        with login(u'user', user.objectID, self.transact) as session:
            deferred = self.facade.createUserWithPassword(
                session, 'fred', 'password', 'Fred', '*****@*****.**')
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testCreateUserByAnonymousRole(self):
        """
        L{FacadeAuthMixin.createUserWithPassword} will raise a
        L{TPathPermissionDenied} if invoked by the anonymous user.
        """
        anonymous = self.system.users[u'anon']
        self.store.commit()

        with login(u'anon', anonymous.objectID, self.transact) as session:
            deferred = self.facade.createUserWithPassword(
                session, 'fred', 'password', 'Fred', '*****@*****.**')
            yield self.assertFailure(deferred, TPathPermissionDenied)
Ejemplo n.º 20
0
class FacadeTagMixinTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeTagMixinTest, self).setUp()
        createSystemData()
        self.transact = Transact(self.threadPool)
        factory = FluidinfoSessionFactory('API-9000')
        self.facade = Facade(self.transact, factory)
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        self.permissions = CachingPermissionAPI(self.user)

    @inlineCallbacks
    def testGetTagWithoutData(self):
        """
        L{FacadeTagMixin.getTag} raises a L{TNonexistentTag} exception if the
        requested L{Tag.path} doesn't exist.
        """
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.getTag(session, u'username/unknown', False)
            yield self.assertFailure(deferred, TNonexistentTag)

    @inlineCallbacks
    def testGetTag(self):
        """
        L{FacadeTagMixin.getTag} returns a L{TTag} instance with information
        about the requested L{Tag}.
        """
        result = TagAPI(self.user).create([(u'username/tag', u'description')])
        [(objectID, path)] = result
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            result = yield self.facade.getTag(session, u'username/tag', False)
            self.assertEqual(str(objectID), result.objectId)
            self.assertEqual(u'username/tag', result.path)
            self.assertTrue(result.indexed)

    @inlineCallbacks
    def testGetTagWithDescription(self):
        """
        L{FacadeTagMixin.getTag} includes the L{Tag.description}, if it was
        requested.
        """
        TagAPI(self.user).create([(u'username/tag', u'A tag.')])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            result = yield self.facade.getTag(session, u'username/tag', True)
            self.assertEqual(u'A tag.', result.description)

    @inlineCallbacks
    def testCreateTag(self):
        """L{FacadeTagMixin.createTag} creates a new L{Tag}."""
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            objectID = yield self.facade.createTag(
                session, u'username', u'tag', u'A tag.', 'ignored', 'ignored')
            self.assertNotIdentical(None, objectID)

        self.store.rollback()
        tag = getTags(paths=[u'username/tag']).one()
        self.assertIdentical(self.user, tag.creator)
        self.assertIdentical(self.user.namespace, tag.namespace)
        self.assertEqual(u'username/tag', tag.path)
        self.assertEqual(u'tag', tag.name)
        self.assertEqual(objectID, str(tag.objectID))

    @inlineCallbacks
    def testCreateTagWithExistingPath(self):
        """
        L{FacadeTagMixin.createTag} raises a L{TTagAlreadyExists} exception if
        the new L{Tag} already exists.
        """
        TagAPI(self.user).create([(u'username/name', u'A tag.')])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.createTag(
                session, u'username', u'name', u'A tag.', 'ignored', 'ignored')
            yield self.assertFailure(deferred, TTagAlreadyExists)

    @inlineCallbacks
    def testCreateTagWithInvalidPath(self):
        """
        L{FacadeTagMixin.createTag} raises a L{TInvalidPath} exception if the
        path of the L{Tag} is not well formed.
        """
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.createTag(session, u'username', u'bad name',
                                             u'A tag.', 'ignored', 'ignored')
            yield self.assertFailure(deferred, TInvalidPath)

    @inlineCallbacks
    def testCreateTagWithUnknownParent(self):
        """
        L{FacadeTagMixin.createTag} raises a L{TNonexistentTag} exception if a
        non-existent parent L{Tag} is specified.
        """
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.createTag(
                session, u'unknown', u'name', u'A tag.', 'ignored', 'ignored')
            yield self.assertFailure(deferred, TNonexistentTag)

    @inlineCallbacks
    def testCreateIsDenied(self):
        """
        L{Facade.createTag} raises a L{TPathPermissionDenied} exception if the
        user doesn't have C{CREATE} permissions on the parent L{Namespace}.
        """
        self.permissions.set([(u'username', Operation.CREATE_NAMESPACE,
                               Policy.CLOSED, [])])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.createTag(
                session, u'username', u'test', u'A tag.', 'ignored', 'ignored')
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testUpdateTag(self):
        """
        L{FacadeTagMixin.updateTag} updates the description for an existing
        L{Tag}.
        """
        tags = TagAPI(self.user)
        tags.create([(u'username/name', u'A tag.')])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            yield self.facade.updateTag(session, u'username/name',
                                        u'A new description.')

        self.store.rollback()
        result = tags.get([u'username/name'], withDescriptions=True)
        self.assertEqual(u'A new description.',
                         result[u'username/name']['description'])

    @inlineCallbacks
    def testUpdateTagWithUnknownPath(self):
        """
        L{FacadeTagMixin.updateTag} raises a L{TNonexistentTag} exception if
        the requested L{Tag.path} doesn't exist.
        """
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.updateTag(session, u'username/unknown',
                                             u'A new description.')
            yield self.assertFailure(deferred, TNonexistentTag)

    @inlineCallbacks
    def testUpdateIsDenied(self):
        """
        L{Facade.updateTag} raises a L{TPathPermissionDenied} exception if the
        user doesn't have C{UPDATE} permissions on the specified L{Tag}.
        """
        TagAPI(self.user).create([(u'username/test', u'A tag.')])
        self.permissions.set([(u'username/test', Operation.UPDATE_TAG,
                               Policy.CLOSED, [])])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.updateTag(session, u'username/test',
                                             u'A new description.')
            yield self.assertFailure(deferred, TPathPermissionDenied)

    @inlineCallbacks
    def testDeleteTag(self):
        """L{FacadeTagMixin.deleteTag} deletes a L{Tag}."""
        tags = TagAPI(self.user)
        tags.create([(u'username/name', u'A tag.')])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            yield self.facade.deleteTag(session, u'username/name')

        self.store.rollback()
        self.assertEqual({}, tags.get([u'username/name']))

    @inlineCallbacks
    def testDeleteTagWithUnknownPath(self):
        """
        L{FacadeTagMixin.deleteTag} raises a L{TNonexistentTag} exception if
        the requested L{Tag.path} doesn't exist.
        """
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.deleteTag(session, u'username/unknown')
            yield self.assertFailure(deferred, TNonexistentTag)

    @inlineCallbacks
    def testDeleteTagWithData(self):
        """
        L{FacadeTagMixin.deleteTag} removes L{TagValue}s associated with the
        deleted L{Tag}.
        """
        objectID = uuid4()
        TagAPI(self.user).create([(u'username/tag', u'A tag.')])
        tagValues = TagValueAPI(self.user)
        tagValues.set({objectID: {u'username/tag': 42}})
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            yield self.facade.deleteTag(session, u'username/tag')

        self.store.rollback()
        self.assertEqual({}, tagValues.get(objectIDs=[objectID],
                                           paths=[u'username/tag']))

    @inlineCallbacks
    def testDeleteIsDenied(self):
        """
        L{Facade.deleteTag} raises a L{TPathPermissionDenied} exception if the
        user doesn't have C{DELETE} permissions on the specified L{Tag}.
        """
        TagAPI(self.user).create([(u'username/test', u'A tag.')])
        self.permissions.set([(u'username/test', Operation.DELETE_TAG,
                               Policy.OPEN, [u'username'])])
        self.store.commit()
        with login(u'username', uuid4(), self.transact) as session:
            deferred = self.facade.deleteTag(session, u'username/test')
            yield self.assertFailure(deferred, TPathPermissionDenied)
Ejemplo n.º 21
0
class FacadeRecentActivityMixinTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()),
                 ('client', IndexResource()),
                 ('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeRecentActivityMixinTest, self).setUp()
        self.system = createSystemData()
        self.transact = Transact(self.threadPool)
        factory = FluidinfoSessionFactory('API-9000')
        self.facade = Facade(self.transact, factory)
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        self.permissions = CachingPermissionAPI(self.user)

    @inlineCallbacks
    def testGetRecentObjectActivity(self):
        """
        L{FacadeRecentActivityMixin.getRecentObjectActivity} returns a C{dict}
        with information about the recent tag values on the given object.
        """
        tagValues = TagValueAPI(self.user)
        objectID1 = ObjectAPI(self.user).create(u'object1')
        objectID2 = uuid4()

        # Use commit() frequently to have different timestamps on each value.
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag1': u'A'}})
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag2': u'B'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user/tag1': u'C'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user/tag2': u'D'}})
        self.store.commit()
        tagValues.set({uuid4(): {u'user/tag1': u'E'}})
        self.store.commit()
        tagValues.set({uuid4(): {u'user/tag2': u'F'}})
        self.store.commit()

        expected = [
            {'tag': u'user/tag2',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'B',
             'username': u'username'},

            {'tag': u'user/tag1',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'A',
             'username': u'username'},

            {'tag': u'fluiddb/about',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'object1',
             'username': u'fluiddb'}]

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentObjectActivity(
                session, str(objectID1))

            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']

            self.assertEqual(expected, result)

    @inlineCallbacks
    def testGetRecentAboutActivity(self):
        """
        L{FacadeRecentActivityMixin.getRecentAboutActivity} returns a C{dict}
        with information about the recent tag values on the given object.
        """
        tagValues = TagValueAPI(self.user)
        objectID1 = ObjectAPI(self.user).create(u'object1')
        objectID2 = uuid4()

        # Use commit() frequently to have different timestamps on each value.
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag1': u'A'}})
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag2': u'B'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user/tag1': u'C'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user/tag2': u'D'}})
        self.store.commit()
        tagValues.set({uuid4(): {u'user/tag1': u'E'}})
        self.store.commit()
        tagValues.set({uuid4(): {u'user/tag2': u'F'}})
        self.store.commit()

        expected = [
            {'tag': u'user/tag2',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'B',
             'username': u'username'},

            {'tag': u'user/tag1',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'A',
             'username': u'username'},

            {'tag': u'fluiddb/about',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'object1',
             'username': u'fluiddb'}]

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentAboutActivity(
                session, 'object1')

            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']

            self.assertEqual(expected, result)

    @inlineCallbacks
    def testGetRecentAboutActivityWithUnkownAboutValue(self):
        """
        L{FacadeRecentActivityMixin.getRecentAboutActivity} returns an empty
        C{list} if the about value doesn't exist.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentAboutActivity(session,
                                                              'unknown')
            self.assertEqual([], result)

    @inlineCallbacks
    def testGetRecentUserActivity(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivity} returns a C{dict}
        with information about the recent tag values on the given user.
        """
        tagValues = TagValueAPI(self.user)
        objectID1 = ObjectAPI(self.user).create(u'object1')
        objectID2 = uuid4()

        # Use commit() frequently to have different timestamps on each value.
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag1': u'A'}})
        self.store.commit()
        tagValues.set({objectID1: {u'user/tag2': u'B'}})
        self.store.commit()

        UserAPI().create([(u'user2', u'secret', u'User', u'*****@*****.**')])
        tagValues = TagValueAPI(getUser(u'user2'))

        tagValues.set({objectID1: {u'user2/tag1': u'C'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user2/tag2': u'D'}})
        self.store.commit()

        UserAPI().create([(u'user3', u'secret', u'User', u'*****@*****.**')])
        tagValues = TagValueAPI(getUser(u'user3'))

        tagValues.set({objectID1: {u'user3/tag1': u'C'}})
        self.store.commit()
        tagValues.set({objectID2: {u'user3/tag2': u'D'}})
        self.store.commit()

        expected = [
            {'tag': u'user/tag2',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'B',
             'username': u'username'},

            {'tag': u'user/tag1',
             'id': str(objectID1),
             'about': u'object1',
             'value': u'A',
             'username': u'username'}]

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentUserActivity(
                session, self.user.username.encode('utf-8'))
            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']
            self.assertEqual(expected, result)

    @inlineCallbacks
    def testGetRecentUserActivityWithUnkownUser(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivity} raises
        L{TNoSuchUser} if the given user doesn't exist.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = self.facade.getRecentUserActivity(session, 'unknown')
            yield self.assertFailure(result, TNoSuchUser)

    @inlineCallbacks
    def testGetRecentActivityForQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentActivityForQuery} returns a
        C{dict} with information about the recent tag values on the objects
        returned by the given query.
        """
        tagValues = TagValueAPI(self.user)
        objectID1 = ObjectAPI(self.user).create(u'object1')

        # Use commit() frequently to have different timestamps on each value.
        self.store.commit()
        tagValues.set({objectID1: {u'user/following': u'A'}})
        self.store.commit()

        runDataImportHandler(self.client.url)

        expected = [{'about': u'object1',
                     'id': str(objectID1),
                     'tag': u'user/following',
                     'username': u'username',
                     'value': u'A'},
                    {'about': u'object1',
                     'id': str(objectID1),
                     'tag': u'fluiddb/about',
                     'username': u'fluiddb',
                     'value': u'object1'}]

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentActivityForQuery(
                session, u'has user/following')

            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']

            self.assertEqual(expected, result)

    @inlineCallbacks
    def testGetRecentActivityForQueryWithBadQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentActivityForQuery} raises
        L{TParseError} if the given query can't be parsed.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = self.facade.getRecentActivityForQuery(session, 'bad')
            yield self.assertFailure(result, TParseError)

    @inlineCallbacks
    def testGetRecentActivityForQueryWithIllegalQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentActivityForQuery} raises
        L{TBadRequest} if the given query contains illegal sub queries.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = self.facade.getRecentActivityForQuery(
                session, 'has fluiddb/about')
            yield self.assertFailure(result, TBadRequest)

    @inlineCallbacks
    def testGetRecentUserActivityForQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivityForQuery} returns a
        C{dict} with information about the recent tag values by the users whose
        objects are returned by the given query.
        """
        UserAPI().create([(u'user2', u'password', u'User',
                           u'*****@*****.**')])
        user2 = getUser(u'user2')

        objectID1 = uuid4()

        # Use commit() frequently to have different timestamps on each value.
        TagValueAPI(self.user).set({objectID1: {u'username/test': u'A'}})
        self.store.commit()
        TagValueAPI(user2).set({objectID1: {u'user2/test': u'B'}})
        self.store.commit()

        runDataImportHandler(self.client.url)

        expected = [{'about': None,
                     'id': str(objectID1),
                     'tag': u'user2/test',
                     'username': u'user2',
                     'value': u'B'},
                    {'about': None,
                     'id': str(objectID1),
                     'tag': u'username/test',
                     'username': u'username',
                     'value': u'A'}]

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentUserActivityForQuery(
                session,
                u'fluiddb/users/username = "******" '
                'OR fluiddb/users/username = "******"')

            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']

            self.assertEqual(expected, result)

    @inlineCallbacks
    def testGetRecentUserActivityForQueryWithBadQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivityForQuery} raises
        L{TParseError} if the given query can't be parsed.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = self.facade.getRecentUserActivityForQuery(session, 'bad')
            yield self.assertFailure(result, TParseError)

    @inlineCallbacks
    def testGetRecentUserActivityForQueryWithIllegalQuery(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivityForQuery} raises
        L{TBadRequest} if the given query contains illegal sub queries.
        """
        with login(self.user.username, uuid4(), self.transact) as session:
            result = self.facade.getRecentUserActivityForQuery(
                session, 'has fluiddb/about')
            yield self.assertFailure(result, TBadRequest)

    @inlineCallbacks
    def testGetRecentUserActivityForQueryWithObjectsNotUsers(self):
        """
        L{FacadeRecentActivityMixin.getRecentUserActivityForQuery} returns an
        empty result if the objects returned by the query are not users.
        """
        tagValues = TagValueAPI(self.user)
        objectID1 = ObjectAPI(self.user).create(u'object1')

        # Use commit() frequently to have different timestamps on each value.
        self.store.commit()
        tagValues.set({objectID1: {u'user/following': u'A'}})
        self.store.commit()

        runDataImportHandler(self.client.url)

        with login(self.user.username, uuid4(), self.transact) as session:
            result = yield self.facade.getRecentUserActivityForQuery(
                session, u'has user/following')

            # Remove the creation times from the result.
            for item in result:
                del item['updated-at']

            self.assertEqual([], result)
Ejemplo n.º 22
0
class FluidinfoSessionTest(FluidinfoTestCase):

    resources = [('config', ConfigResource()),
                 ('fs', TemporaryDirectoryResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FluidinfoSessionTest, self).setUp()
        self.transact = Transact(self.threadPool)

    def testLogin(self):
        """L{AuthenticationPlugin.login} stores the provided credentials."""
        objectID = uuid4()
        session = FluidinfoSession('id', self.transact)
        session.start()
        try:
            session.auth.login(u'username', objectID)
        finally:
            session.stop()
        self.assertEqual(u'username', session.auth.username)
        self.assertEqual(objectID, session.auth.objectID)

    def testLoginWithoutStart(self):
        """
        L{AuthenticationPlugin.login} raises a C{RuntimeError} if the session
        hasn't been started.
        """
        session = FluidinfoSession('id', self.transact)
        self.assertRaises(RuntimeError, session.auth.login, u'username',
                          uuid4())

    def testUser(self):
        """
        The L{AuthenticationPlugin.user} property returns the L{User} instance
        for the session.
        """
        createSystemData()
        [(objectID, username)] = UserAPI().create([
            (u'user', u'secret', u'User', u'*****@*****.**')
        ])
        session = FluidinfoSession('id', self.transact)
        session.start()
        try:
            session.auth.login(username, objectID)
        finally:
            session.stop()
        self.assertEqual(u'user', session.auth.user.username)

    def testDumpsAndLoads(self):
        """
        Data stored by a L{LoggingPlugin} can be dumped to and loaded from
        JSON.
        """
        objectID = uuid4()
        session = FluidinfoSession('id', self.transact)
        session.start()
        try:
            session.auth.login(u'username', objectID)
        finally:
            session.stop()
        data = session.dumps()
        loadedSession = FluidinfoSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(session.auth.username, loadedSession.auth.username)
        self.assertEqual(session.auth.objectID, loadedSession.auth.objectID)
Ejemplo n.º 23
0
class FacadeAuthenticateUserWithOAuthTest(FluidinfoTestCase):
    """Test L{Facade} OAuth authentication."""

    resources = [('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeAuthenticateUserWithOAuthTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)
        self.system = createSystemData()

    @inlineCallbacks
    def testAuthenticateUserWithOAuth(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        consumer = api.register(consumerUser)
        token = api.getAccessToken(consumerUser, user)

        self.store.commit()
        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        request = Request.from_request('GET', u'https://fluidinfo.com/foo',
                                       headers, {'argument1': 'bar'})
        signature = SignatureMethod_HMAC_SHA1().sign(request,
                                                     consumer, None)
        nonce = 'nonce'
        credentials = OAuthCredentials(
            'fluidinfo.com', consumerUser.username, token.encrypt(),
            'HMAC-SHA1', signature, timestamp, nonce, 'GET',
            u'https://fluidinfo.com/foo', headers, arguments)
        session = yield self.facade.authenticateUserWithOAuth(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateUserWithOAuthIgnoresCase(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} ignores the case in the
        consumer key.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        consumer = api.register(consumerUser)
        token = api.getAccessToken(consumerUser, user)

        self.store.commit()
        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        request = Request.from_request('GET', u'https://fluidinfo.com/foo',
                                       headers, {'argument1': 'bar'})
        signature = SignatureMethod_HMAC_SHA1().sign(request,
                                                     consumer, None)
        nonce = 'nonce'
        credentials = OAuthCredentials(
            'fluidinfo.com', u'ConsumeR', token.encrypt(),
            'HMAC-SHA1', signature, timestamp, nonce, 'GET',
            u'https://fluidinfo.com/foo', headers, arguments)
        session = yield self.facade.authenticateUserWithOAuth(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    @inlineCallbacks
    def testAuthenticateUserWithOAuthWithMixedCaseInToken(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} ignores the case in the
        username in the token.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        consumer = api.register(consumerUser)
        token = dataToToken(consumer.secret,
                            {'username': u'UseR',
                             'creationTime': '20121228-161823'})

        self.store.commit()
        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        request = Request.from_request('GET', u'https://fluidinfo.com/foo',
                                       headers, {'argument1': 'bar'})
        signature = SignatureMethod_HMAC_SHA1().sign(request,
                                                     consumer, None)
        nonce = 'nonce'
        credentials = OAuthCredentials(
            'fluidinfo.com', consumerUser.username, token,
            'HMAC-SHA1', signature, timestamp, nonce, 'GET',
            u'https://fluidinfo.com/foo', headers, arguments)
        session = yield self.facade.authenticateUserWithOAuth(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    def testAuthenticateUserWithOAuthIncorrectSignature(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} raises a
        L{TPasswordIncorrect} exception if the signature in the OAuth
        credentials is incorrect.
        """
        UserAPI().create([
            (u'consumer', u'secret', u'Consumer', u'*****@*****.**'),
            (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        api.register(consumerUser)
        token = api.getAccessToken(consumerUser, user)

        self.store.commit()
        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        signature = 'wrong'
        nonce = 'nonce'
        credentials = OAuthCredentials(
            'fluidinfo.com', user.username, token.encrypt(), u'HMAC-SHA1',
            signature, timestamp, nonce, 'GET', 'https://fluidinfo.com/foo',
            headers, arguments)
        deferred = self.facade.authenticateUserWithOAuth(credentials)
        return self.assertFailure(deferred, TPasswordIncorrect)

    def testAuthenticateUserWithOAuthUnknownConsumer(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} raises
        L{TNoSuchUser} if the consumer does not exist.
        """
        user2 = createUser(u'user2', u'pass2', u'User2', u'*****@*****.**')
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        token = dataToToken('a' * 16, {'username': user2.username})
        signature = '3MNZYSgsGftopjuwv3g2u5Q+MZM='
        nonce = 'nonce'

        credentials = OAuthCredentials(
            'fluidinfo.com', u'user1', token, 'HMAC-SHA1', signature,
            timestamp, nonce, 'GET', u'https://fluidinfo.com/foo', headers,
            arguments)
        deferred = self.facade.authenticateUserWithOAuth(credentials)

        return self.assertFailure(deferred, TNoSuchUser)

    def testAuthenticateUserWithOAuthUnregisteredConsumer(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} raises
        L{TPasswordIncorrect} if the consumer exists as a Fluidinfo user
        but is not registered as an OAuth consumer.
        """
        user1 = createUser(u'user1', u'pass1', u'User1', u'*****@*****.**')
        user2 = createUser(u'user2', u'pass2', u'User2', u'*****@*****.**')
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        token = dataToToken('a' * 16, {'username': user2.username})
        signature = '3MNZYSgsGftopjuwv3g2u5Q+MZM='
        nonce = 'nonce'

        credentials = OAuthCredentials(
            'fluidinfo.com', user1.username, token, 'HMAC-SHA1', signature,
            timestamp, nonce, 'GET', u'https://fluidinfo.com/foo', headers,
            arguments)
        deferred = self.facade.authenticateUserWithOAuth(credentials)

        return self.assertFailure(deferred, TPasswordIncorrect)

    def testAuthenticateUserWithOAuthUnknownUsernameInToken(self):
        """
        L{FacadeAuthMixin.authenticateUserWithOAuth} raises a
        L{TNoSuchUser} exception if the username in the token does
        not match an existing L{User}.
        """
        user1 = createUser(u'user1', u'pass1', u'User1', u'*****@*****.**')
        oauthConsumer1 = createOAuthConsumer(user1, secret='secret16charlng1')
        self.store.commit()

        timestamp = 1314976811
        headers = {'header1': 'foo'}
        arguments = 'argument1=bar'
        token = dataToToken(oauthConsumer1.secret,
                            {'username': u'unknownUser'})
        signature = '3MNZYSgsGftopjuwv3g2u5Q+MZM='
        nonce = 'nonce'

        credentials = OAuthCredentials(
            'fluidinfo.com', user1.username, token, u'HMAC-SHA1', signature,
            timestamp, nonce, 'GET', 'https://fluidinfo.com/foo', headers,
            arguments)
        deferred = self.facade.authenticateUserWithOAuth(credentials)

        return self.assertFailure(deferred, TNoSuchUser)
Ejemplo n.º 24
0
class OAuthEchoResourceTest(FluidinfoTestCase, FakeReactorAndConnectMixin):

    resources = [('config', ConfigResource()), ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(OAuthEchoResourceTest, self).setUp()
        self.agent = Agent(self.FakeReactor())
        self.transact = Transact(self.threadPool)
        system = createSystemData()
        self.anonymous = system.users[u'anon']
        OAuthConsumerAPI().register(self.anonymous)
        self.agent = Agent(self.FakeReactor())

    def testRenderWithMissingServiceProviderHeader(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if the
        C{X-Auth-Service-Provider} header is not in the request, and the
        C{X-Fluiddb-*} response headers indicate that the header is missing.
        """
        headers = {'X-Verify-Credentials-Authorization': ['OAuth ...']}
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session)
            self.assertEqual('', resource.render_GET(request))
            headers = dict(request.responseHeaders.getAllRawHeaders())
            yield resource.deferred
            self.assertEqual(
                {
                    'X-Fluiddb-Error-Class': ['MissingHeader'],
                    'X-Fluiddb-Header': ['X-Auth-Service-Provider'],
                    'X-Fluiddb-Request-Id': [session.id]
                }, headers)

    def testRenderWithUnknownServiceProvider(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if the
        C{X-Auth-Service-Provider} in the header is not supported.
        """
        headers = {
            'X-Verify-Credentials-Authorization': ['OAuth ...'],
            'X-Auth-Service-Provider': ['https://example.com/1/verify']
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session)
            self.assertEqual('', resource.render_GET(request))
            headers = dict(request.responseHeaders.getAllRawHeaders())
            yield resource.deferred
            self.assertEqual(
                {
                    'X-Fluiddb-Error-Class': ['UnknownServiceProvider'],
                    'X-Fluiddb-Request-Id': [session.id]
                }, headers)

    def testRenderWithMissingAuthorizationHeader(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if the
        C{X-Verify-Credentials-Authorization} header is not in the request,
        and the C{X-Fluiddb-*} response headers indicate that the header is
        missing.
        """
        headers = {'X-Auth-Service-Provider': [TWITTER_URL]}
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session)
            self.assertEqual('', resource.render_GET(request))
            headers = dict(request.responseHeaders.getAllRawHeaders())
            yield resource.deferred
            self.assertEqual(
                {
                    'X-Fluiddb-Error-Class': ['MissingHeader'],
                    'X-Fluiddb-Header': ['X-Verify-Credentials-Authorization'],
                    'X-Fluiddb-Request-Id': [session.id]
                }, headers)

    def testRenderWithUnsupportedAuthorizationHeader(self):
        """
        A C{BAD_REQUEST} HTTP status code is returned if the
        C{X-Verify-Credentials-Authorization} in the header doesn't use the
        C{OAuth} scheme.
        """
        headers = {
            'X-Verify-Credentials-Authorization': ['Basic ...'],
            'X-Auth-Service-Provider': [TWITTER_URL]
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session)
            self.assertEqual('', resource.render_GET(request))
            headers = dict(request.responseHeaders.getAllRawHeaders())
            yield resource.deferred
            self.assertEqual(
                {
                    'X-Fluiddb-Error-Class': ['BadHeader'],
                    'X-Fluiddb-Header': ['X-Verify-Credentials-Authorization'],
                    'X-Fluiddb-Request-Id': [session.id]
                }, headers)

    @inlineCallbacks
    def testRenderWithSuccessfulVerification(self):
        """
        An C{OK} HTTP status code is returned if the authorization is
        successfully verified by the service provider.  The results of the
        call to the service provider to verify credentials are returned to the
        user.
        """
        UserAPI().create([(u'john', 'secret', u'John', u'*****@*****.**')])
        TwitterUserAPI().create(u'john', 1984245)
        self.store.commit()

        self.agent._connect = self._connect
        headers = {
            'X-Verify-Credentials-Authorization': ['OAuth ...'],
            'X-Auth-Service-Provider': [TWITTER_URL]
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session, self.agent)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))

            [(_, responseDeferred)] = self.protocol.requests
            data = {'id': 1984245, 'screen_name': u'john', 'name': u'John'}
            response = FakeResponse(ResponseDone(), dumps(data))
            responseDeferred.callback(response)
            result = yield resource.deferred
            self.assertTrue(result['access-token'])
            self.assertTrue(result['renewal-token'])
            del result['access-token']
            del result['renewal-token']
            self.assertEqual(
                {
                    'username': u'john',
                    'new-user': False,
                    'missing-password': False,
                    'data': data,
                    'uid': 1984245
                }, result)
            self.assertEqual(OK, request.code)
            self.assertEqual(data, loads(request.written.getvalue()))

    @inlineCallbacks
    def testRenderWithNewUser(self):
        """
        Missing L{User}s are created automatically and linked to
        L{TwitterUser}s for authorized UIDs.
        """
        self.assertNotIn(u'john', UserAPI().get([u'john']))
        self.store.commit()

        self.agent._connect = self._connect
        headers = {
            'X-Verify-Credentials-Authorization': ['OAuth ...'],
            'X-Auth-Service-Provider': [TWITTER_URL]
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session, self.agent)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))

            [(_, responseDeferred)] = self.protocol.requests
            data = {'id': 1984245, 'screen_name': u'john', 'name': u'John'}
            response = FakeResponse(ResponseDone(), dumps(data))
            responseDeferred.callback(response)
            result = yield resource.deferred
            self.assertTrue(result['access-token'])
            self.assertTrue(result['renewal-token'])
            del result['access-token']
            del result['renewal-token']
            self.assertEqual(
                {
                    'username': u'john',
                    'new-user': True,
                    'missing-password': True,
                    'data': data,
                    'uid': 1984245
                }, result)
            self.assertEqual(OK, request.code)
            self.assertEqual(data, loads(request.written.getvalue()))
            headers = dict(request.responseHeaders.getAllRawHeaders())
            self.assertTrue(headers['X-Fluiddb-Access-Token'])
            self.assertTrue(headers['X-Fluiddb-Renewal-Token'])
            del headers['X-Fluiddb-Access-Token']
            del headers['X-Fluiddb-Renewal-Token']
            self.assertEqual(
                {
                    'X-Fluiddb-New-User': ['true'],
                    'X-Fluiddb-Missing-Password': ['true'],
                    'X-Fluiddb-Username': ['am9obg==']
                },  # username is in base64.
                headers)

            self.store.rollback()
            self.assertIn(u'john', UserAPI().get([u'john']))

    @inlineCallbacks
    def testRenderWithUserConflict(self):
        """
        A C{CONFLICT} HTTP status code is returned if the authorization is
        successfully verified by the service provider, but the username
        clashes with an existing L{User} that isn't linked to the Twitter UID.
        The offending username is returned UTF-8 and base64 encoded.
        """
        username = u'john\N{HIRAGANA LETTER A}'
        UserAPI().create([(username, 'secret', u'John', u'*****@*****.**')])
        self.store.commit()

        self.agent._connect = self._connect
        headers = {
            'X-Verify-Credentials-Authorization': ['OAuth ...'],
            'X-Auth-Service-Provider': [TWITTER_URL]
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session, self.agent)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))

            [(_, responseDeferred)] = self.protocol.requests
            data = {'id': 1984245, 'screen_name': username, 'name': u'John'}
            response = FakeResponse(ResponseDone(), dumps(data))
            responseDeferred.callback(response)
            yield resource.deferred
            self.assertEqual(CONFLICT, request.code)
            headers = dict(request.responseHeaders.getAllRawHeaders())
            encodedUsername = b64encode(username.encode('utf-8'))
            self.assertEqual(
                {
                    'X-Fluiddb-Error-Class': ['UsernameConflict'],
                    'X-Fluiddb-Username': [encodedUsername],
                    'X-Fluiddb-Request-Id': [session.id]
                }, headers)

    @inlineCallbacks
    def testRenderWithFailingServiceProviderCall(self):
        """
        A C{SERVICE_UNAVAILABLE} HTTP status code is returned if an error
        occurs while communicating with the L{ServiceProvider}.
        """
        UserAPI().create([(u'user', 'secret', u'User', u'*****@*****.**')])
        TwitterUserAPI().create(u'user', 1984245)
        self.store.commit()

        self.agent._connect = self._connect
        headers = {
            'X-Verify-Credentials-Authorization': ['OAuth ...'],
            'X-Auth-Service-Provider': [TWITTER_URL]
        }
        request = FakeRequest(headers=Headers(headers))
        with login(u'anon', self.anonymous.objectID, self.transact) as session:
            resource = OAuthEchoResource(session, self.agent)
            self.assertEqual(NOT_DONE_YET, resource.render_GET(request))

            [(_, responseDeferred)] = self.protocol.requests
            response = FakeResponse(RuntimeError(), None)
            responseDeferred.callback(response)
            yield resource.deferred
            self.assertEqual(SERVICE_UNAVAILABLE, request.code)
Ejemplo n.º 25
0
class LoggingPluginTest(FluidinfoTestCase):

    resources = [('log', LoggingResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(LoggingPluginTest, self).setUp()
        self.transact = Transact(self.threadPool)

    def testInfo(self):
        """L{Session.info} logs a message using the C{INFO} log level."""
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.log.info('Hello, world!')
        finally:
            session.stop()

        self.assertIn('INFO', session.log.messages)
        self.assertIn('Hello, world!', session.log.messages)

    def testError(self):
        """L{Session.error} logs a message using the C{ERROR} log level."""
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.log.error('Something bad happened.')
        finally:
            session.stop()

        self.assertIn('ERROR', session.log.messages)
        self.assertIn('Something bad happened.', session.log.messages)

    def testException(self):
        """
        L{Session.exception} logs an exception using the C{ERROR} log level.
        """
        session = SampleSession('id', self.transact)
        session.start()
        try:
            error = RuntimeError('Something bad happened.')
            session.log.exception(error)
        finally:
            session.stop()

        self.assertIn('ERROR', session.log.messages)
        self.assertIn('Something bad happened.', session.log.messages)

    def testDumpsAndLoads(self):
        """
        Data stored by a L{LoggingPlugin} can be dumped to and loaded from
        JSON.
        """
        session = SampleSession('id', self.transact)
        session.start()
        try:
            session.log.info('Hello, world!')
        finally:
            session.stop()

        data = session.dumps()
        loadedSession = SampleSession('another-id', self.transact)
        loadedSession.loads(data)
        self.assertEqual(session.log.messages, loadedSession.log.messages)
Ejemplo n.º 26
0
class FacadeOAuth2CheckerTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeOAuth2CheckerTest, self).setUp()
        factory = FluidinfoSessionFactory('API-9000')
        transact = Transact(self.threadPool)
        createSystemData()
        self.checker = FacadeOAuth2Checker()
        self.checker.facadeClient = Facade(transact, factory)

    @inlineCallbacks
    def testRequestAvatarId(self):
        """
        L{FacadeOAuth2Checker.requestAvatarId} creates a
        L{FluidinfoSession} for the authenticated user only if credentials are
        correct.
        """
        UserAPI().create([(u'consumer', u'secret', u'Consumer',
                           u'*****@*****.**'),
                          (u'user', u'secret', u'User', u'*****@*****.**')])
        consumerUser = getUser(u'consumer')
        user = getUser(u'user')
        api = OAuthConsumerAPI()
        api.register(consumerUser)
        token = api.getAccessToken(consumerUser, user)
        self.store.commit()

        credentials = OAuth2Credentials(u'consumer', 'secret', token.encrypt())
        session = yield self.checker.requestAvatarId(credentials)
        self.assertEqual(user.username, session.auth.username)
        self.assertEqual(user.objectID, session.auth.objectID)

    def testRequestAvatarIdWithTokenMadeFromWrongSecret(self):
        """
        L{FacadeOAuth2Checker.requestAvatarId} creates a
        L{FluidinfoSession} for the authenticated user only if the access
        token was created using the consumer's secret.
        """
        user1 = createUser(u'user1', u'pass1', u'User1', u'*****@*****.**')
        createOAuthConsumer(user1, secret='secret16charlng1')
        user2 = createUser(u'user2', u'pass2', u'User2', u'*****@*****.**')
        self.store.commit()
        token = dataToToken('a' * 16, {'username': user2.username})
        credentials = OAuth2Credentials(u'user1', u'pass1', token)
        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)

    def testRequestAvatarIdWithInvalidToken(self):
        """
        L{FacadeOAuth2Checker.requestAvatarId} creates a
        L{FluidinfoSession} for the authenticated user only if the access
        token was properly formed (by calling dataToToken).
        """
        user = createUser(u'user', u'pass', u'User', u'*****@*****.**')
        createOAuthConsumer(user, secret='secret16charlng1')
        self.store.commit()
        credentials = OAuth2Credentials(u'user', u'pass', token='xxx')
        deferred = self.checker.requestAvatarId(credentials)
        return self.assertFailure(deferred, UnauthorizedLogin)
Ejemplo n.º 27
0
class DelegatorTest(FluidinfoTestCase, FakeReactorAndConnectMixin):

    resources = [('config', ConfigResource()),
                 ('log', LoggingResource()),
                 ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(DelegatorTest, self).setUp()
        self.agent = Agent(self.FakeReactor())
        self.transact = Transact(self.threadPool)
        createSystemData()

    @inlineCallbacks
    def testGetUser(self):
        """
        L{Delegator.getUser} returns a C{(User, data)} 2-tuple when the
        service provider successfully verifies credentials and a mapping
        between a Fluidinfo L{User} and the L{TwitterUser} being verified
        exists.
        """
        UserAPI().create([
            (u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
            (u'user', 'secret', u'User', u'*****@*****.**')])
        TwitterUserAPI().create(u'user', 1984245)
        consumer = getUser(u'consumer')
        OAuthConsumerAPI().register(consumer)
        self.store.commit()

        self.agent._connect = self._connect
        authentication = 'OAuth oauth_consumer_key="...", ...'
        provider = ServiceProvider(self.agent, 'https://example.com/verify')
        delegator = Delegator(self.transact)
        deferred = delegator.getUser(u'consumer', provider, authentication)

        [(request, responseDeferred)] = self.protocol.requests
        response = FakeResponse(ResponseDone(), dumps({'id': 1984245}))
        responseDeferred.callback(response)
        result = yield deferred
        self.assertTrue(result['access-token'])
        self.assertTrue(result['renewal-token'])
        del result['access-token']
        del result['renewal-token']
        self.assertEqual({'username': u'user',
                          'new-user': False,
                          'missing-password': False,
                          'uid': 1984245,
                          'data': {u'id': 1984245}},
                         result)

    @inlineCallbacks
    def testGetUserWithNoPassword(self):
        """
        If a L{User} returned by L{Delegator.getUser} doesn't have a password,
        a C{missing-password} value is added to the result.
        """
        UserAPI().create([
            (u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
            (u'user', None, u'User', u'*****@*****.**')])
        TwitterUserAPI().create(u'user', 1984245)
        consumer = getUser(u'consumer')
        OAuthConsumerAPI().register(consumer)
        self.store.commit()

        self.agent._connect = self._connect
        authentication = 'OAuth oauth_consumer_key="...", ...'
        provider = ServiceProvider(self.agent, 'https://example.com/verify')
        delegator = Delegator(self.transact)
        deferred = delegator.getUser(u'consumer', provider, authentication)

        [(request, responseDeferred)] = self.protocol.requests
        response = FakeResponse(ResponseDone(), dumps({'id': 1984245}))
        responseDeferred.callback(response)
        result = yield deferred
        self.assertTrue(result['access-token'])
        self.assertTrue(result['renewal-token'])
        del result['access-token']
        del result['renewal-token']
        self.assertEqual({'username': u'user',
                          'new-user': False,
                          'missing-password': True,
                          'uid': 1984245,
                          'data': {u'id': 1984245}},
                         result)

    @inlineCallbacks
    def testGetUserWithNewUser(self):
        """
        A new L{User} is created if L{Delegator.getUser} verifies the
        L{TwitterUser} but can't find a user matching the Twitter screen name.
        A C{new-user} value is returned to indicate this situation.
        """
        UserAPI().create([
            (u'consumer', 'secret', u'Consumer', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        OAuthConsumerAPI().register(consumer)
        self.store.commit()

        self.agent._connect = self._connect
        authentication = 'OAuth oauth_consumer_key="...", ...'
        provider = ServiceProvider(self.agent, 'https://example.com/verify')
        delegator = Delegator(self.transact)
        deferred = delegator.getUser(u'consumer', provider, authentication)

        [(request, responseDeferred)] = self.protocol.requests
        response = FakeResponse(ResponseDone(), dumps({'id': 1984245,
                                                       'screen_name': u'john',
                                                       'name': u'John Doe'}))
        responseDeferred.callback(response)
        result = yield deferred
        self.assertTrue(result['new-user'])

        user = TwitterUserAPI().get(1984245)
        self.assertEqual(u'john', user.username)
        self.assertEqual(u'John Doe', user.fullname)

    @inlineCallbacks
    def testGetUserWithNewUserAndMixedCaseTwitterScreenName(self):
        """
        A new L{User} is created if L{Delegator.getUser} verifies the
        L{TwitterUser} but can't find a user matching the Twitter screen name.
        The Twitter user's screen name should be lowercased to make the
        new Fluidinfo username.
        """
        UserAPI().create([
            (u'consumer', 'secret', u'Consumer', u'*****@*****.**')])
        consumer = getUser(u'consumer')
        OAuthConsumerAPI().register(consumer)
        self.store.commit()

        self.agent._connect = self._connect
        authentication = 'OAuth oauth_consumer_key="...", ...'
        provider = ServiceProvider(self.agent, 'https://example.com/verify')
        delegator = Delegator(self.transact)
        deferred = delegator.getUser(u'consumer', provider, authentication)

        [(request, responseDeferred)] = self.protocol.requests
        response = FakeResponse(ResponseDone(),
                                dumps({'id': 1984245,
                                       'screen_name': u'MixedCaseName',
                                       'name': u'John Doe'}))
        responseDeferred.callback(response)
        result = yield deferred
        self.assertTrue(result['new-user'])
        user = TwitterUserAPI().get(1984245)
        self.assertEqual(u'mixedcasename', user.username)

    @inlineCallbacks
    def testGetUserWithUserConflict(self):
        """
        A L{DuplicateUserError} exception is raised if a L{User} with the same
        username as the Twitter user's screen name already exists, but is not
        associated with the Twitter UID.
        """
        UserAPI().create([
            (u'consumer', 'secret', u'Consumer', u'*****@*****.**'),
            (u'john', 'secret', u'John', u'*****@*****.**')])
        self.store.commit()

        self.agent._connect = self._connect
        authentication = 'OAuth oauth_consumer_key="...", ...'
        consumer = getUser(u'consumer')
        provider = ServiceProvider(self.agent, 'https://example.com/verify')
        delegator = Delegator(self.transact)
        deferred = delegator.getUser(consumer, provider, authentication)

        [(request, responseDeferred)] = self.protocol.requests
        response = FakeResponse(ResponseDone(), dumps({'id': 1984245,
                                                       'screen_name': u'john',
                                                       'name': u'John Doe'}))
        responseDeferred.callback(response)
        error = yield self.assertFailure(deferred, DuplicateUserError)
        self.assertEqual([u'john'], list(error.usernames))
Ejemplo n.º 28
0
class RecentUserActivityResourceTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(RecentUserActivityResourceTest, self).setUp()
        createSystemData()
        UserAPI().create([(u'username', u'password', u'User',
                           u'*****@*****.**')])
        self.user = getUser(u'username')
        factory = FluidinfoSessionFactory('API-9000')
        self.transact = Transact(self.threadPool)
        self.facade = Facade(self.transact, factory)

    @inlineCallbacks
    def testRenderRecentUserActivity(self):
        """
        L{RecentUserActivityResource.deferred_render_GET} renders a response
        with recent activity data for the given user.
        """
        objectID = ObjectAPI(self.user).create(u'object1')
        TagValueAPI(self.user).set({objectID: {u'username/tag1': u'A'}})
        self.store.commit()
        TagValueAPI(self.user).set({objectID: {u'username/tag2': u'B'}})
        self.store.commit()
        request = FakeRequest()
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = RecentUserActivityResource(self.facade, session,
                                                  u'username')
            body = yield resource.deferred_render_GET(request)
            body = json.loads(body)
            expected = [{
                u'username': u'username',
                u'about': u'object1',
                u'id': str(objectID),
                u'tag': u'username/tag2',
                u'value': u'B'
            }, {
                u'username': u'username',
                u'about': u'object1',
                u'id': str(objectID),
                u'tag': u'username/tag1',
                u'value': u'A'
            }]
            # Clean up timestamps.
            for item in body:
                del item['updated-at']
            self.assertEqual(expected, body)
            self.assertEqual(http.OK, request.code)

    @inlineCallbacks
    def testRenderRecentAboutActivityWithUnknownAbout(self):
        """
        L{RecentUserActivityResource.deferred_render_GET} raises L{TNoSuchUser}
        if the given user doesn't exist.
        """
        self.store.commit()
        request = FakeRequest()
        with login(u'username', self.user.objectID, self.transact) as session:
            resource = RecentUserActivityResource(self.facade, session,
                                                  u'unknown')
            deferred = resource.deferred_render_GET(request)
            yield self.assertFailure(deferred, TNoSuchUser)
Ejemplo n.º 29
0
class FacadeUserMixinTest(FluidinfoTestCase):

    resources = [('cache', CacheResource()), ('config', ConfigResource()),
                 ('log', LoggingResource()), ('store', DatabaseResource()),
                 ('threadPool', ThreadPoolResource())]

    def setUp(self):
        super(FacadeUserMixinTest, self).setUp()
        self.transact = Transact(self.threadPool)
        factory = FluidinfoSessionFactory('API-9000')
        self.facade = Facade(self.transact, factory)
        system = createSystemData()
        self.admin = system.users[u'fluiddb']

    @inlineCallbacks
    def testGetUserWithoutData(self):
        """
        L{FacadeUserMixin.getUser} raises a L{TNoSuchUser} exception if the
        requested L{User.username} doesn't exist.
        """
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            deferred = self.facade.getUser(session, u'unknown')
            error = yield self.assertFailure(deferred, TNoSuchUser)
            self.assertEqual(u'unknown', error.name)

    @inlineCallbacks
    def testGetUser(self):
        """
        L{FacadeUserMixin.getUser} returns a L{TUser} instance with
        information about the requested L{User}.
        """
        UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')])
        user = getUser(u'user')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            result = yield self.facade.getUser(session, u'user')
            self.assertEqual(u'user', result.username)
            self.assertEqual(str(user.objectID), result.objectId)
            self.assertEqual(u'User', result.name)
            self.assertEqual(u'USER', result.role)

    @inlineCallbacks
    def testGetUserIgnoresCase(self):
        """L{FacadeUserMixin.getUser} ignores case for the username."""
        UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')])
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            result = yield self.facade.getUser(session, u'uSeR')
            self.assertEqual(u'user', result.username)

    @inlineCallbacks
    def testUpdateUser(self):
        """
        L{FacadeUserMixin.updateUser} updates the description for an existing
        L{User}.
        """
        UserAPI().create([(u'test', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'test')
        passwordHash = user.passwordHash
        self.store.commit()
        info = TUserUpdate(u'test', u'password', u'new-name',
                           u'*****@*****.**')
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'test', user.username)
        self.assertNotEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'new-name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)

    @inlineCallbacks
    def testUpdateUserWithoutPassword(self):
        """
        If a L{User.password} is not passed to L{FacadeUserMixin.updateUser}
        the existing password will not be changed.
        """
        UserAPI().create([(u'user', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'user')
        passwordHash = user.passwordHash
        info = TUserUpdate(u'user', None, u'new-name', u'*****@*****.**',
                           'USER_MANAGER')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'user', user.username)
        self.assertEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'new-name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)
        self.assertEqual(Role.USER_MANAGER, user.role)

    @inlineCallbacks
    def testUpdateUserWithoutName(self):
        """
        If a L{User.fullname} is not passed to L{FacadeUserMixin.updateUser}
        the existing name will not be changed.
        """
        UserAPI().create([(u'user', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'user')
        passwordHash = user.passwordHash
        info = TUserUpdate(u'user', u's3cr3t', None, u'*****@*****.**',
                           'USER_MANAGER')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'user', user.username)
        self.assertNotEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)
        self.assertEqual(Role.USER_MANAGER, user.role)

    @inlineCallbacks
    def testUpdateUserWithoutEmail(self):
        """
        If a L{User.email} is not passed to L{FacadeUserMixin.updateUser}
        the existing email address will not be changed.
        """
        UserAPI().create([(u'user', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'user')
        passwordHash = user.passwordHash
        info = TUserUpdate(u'user', u's3cr3t', u'new-name', None,
                           'USER_MANAGER')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'user', user.username)
        self.assertNotEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'new-name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)
        self.assertEqual(Role.USER_MANAGER, user.role)

    @inlineCallbacks
    def testUpdateUserWithoutRole(self):
        """
        If a L{User.role} is not passed to L{FacadeUserMixin.updateUser}
        the existing user role will not be changed.
        """
        UserAPI().create([(u'user', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'user')
        passwordHash = user.passwordHash
        info = TUserUpdate(u'user', u's3cr3t', u'new-name', '*****@*****.**',
                           None)
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'user', user.username)
        self.assertNotEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'new-name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)
        self.assertEqual(Role.USER, user.role)

    @inlineCallbacks
    def testUpdateUserWithBadRole(self):
        """
        If an invalid L{User.role} is passed to L{FacadeUserMixin.updateUser}
        a L{TBadRequest} exception is raised.
        """
        info = TUserUpdate(u'user', u's3cr3t', u'new-name', '*****@*****.**',
                           'BAD_ROLE')
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            deferred = self.facade.updateUser(session, info)
            yield self.assertFailure(deferred, TBadRequest)

    @inlineCallbacks
    def testUpdateUserIgnoresCase(self):
        """
        L{FacadeUserMixin.updateUser} ignores case when updating a new user.
        """
        UserAPI().create([(u'test', u'secret', u'name', u'*****@*****.**')])
        user = getUser(u'test')
        passwordHash = user.passwordHash
        info = TUserUpdate(u'TesT', u'password', u'new-name',
                           u'*****@*****.**')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.updateUser(session, info)

        self.store.rollback()
        self.assertEqual(u'test', user.username)
        self.assertNotEqual(passwordHash, user.passwordHash)
        self.assertEqual(u'new-name', user.fullname)
        self.assertEqual(u'*****@*****.**', user.email)

    @inlineCallbacks
    def testUpdateUserWithUnknownUsername(self):
        """
        L{FacadeUserMixin.updateUser} raises a L{TNoSuchUser} exception
        if the requested L{User.username} doesn't exist.
        """
        info = TUserUpdate(u'unknown', u'password', u'name',
                           u'*****@*****.**')
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            deferred = self.facade.updateUser(session, info)
            error = yield self.assertFailure(deferred, TNoSuchUser)
            self.assertEqual(u'unknown', error.name)

    @inlineCallbacks
    def testUpdateUserIsDenied(self):
        """
        L{FacadeUserMixin.updateUser} raises a L{TPathPermissionDenied}
        exception if the user making the request is not a superuser.
        """
        UserAPI().create([(u'user', u'secret', u'User', u'*****@*****.**')])
        info = TUserUpdate(u'username', u'secret', u'Username',
                           u'*****@*****.**')
        self.store.commit()
        with login(u'user', uuid4(), self.transact) as session:
            deferred = self.facade.updateUser(session, info)
            error = yield self.assertFailure(deferred, TPathPermissionDenied)
            self.assertEqual(u'username', error.path)

    @inlineCallbacks
    def testDeleteUser(self):
        """L{FacadeUserMixin.deleteUser} deletes a L{User}."""
        UserAPI().create([(u'test', u'secret', u'name', u'*****@*****.**')],
                         createPrivateNamespace=False)
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.deleteUser(session, u'test')

        self.store.rollback()
        self.assertIdentical(None, getUser(u'test'))

    @inlineCallbacks
    def testDeleteUserIgnoresCase(self):
        """
        L{FacadeUserMixin.deleteUser} ignores case for the username when
        deleting a user.
        """
        UserAPI().create([(u'test', u'secret', u'name', u'*****@*****.**')],
                         createPrivateNamespace=False)
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            yield self.facade.deleteUser(session, u'tESt')

        self.store.rollback()
        self.assertIdentical(None, getUser(u'test'))

    @inlineCallbacks
    def testDeleteUserWithUnknownUsername(self):
        """
        L{FacadeUserMixin.deleteUser} raises a L{TNoSuchUser} exception if the
        specified L{User.username} doesn't exist.
        """
        self.store.commit()
        with login(u'fluiddb', self.admin.objectID, self.transact) as session:
            deferred = self.facade.deleteUser(session, u'unknown')
            error = yield self.assertFailure(deferred, TNoSuchUser)
            self.assertEqual(u'unknown', error.name)

    @inlineCallbacks
    def testDeleteUserIsDenied(self):
        """
        L{FacadeUserMixin.deleteUser} raises a L{TPathPermissionDenied}
        exception if the user making the request is not a superuser.
        """
        [(objectID, username)] = UserAPI().create([
            (u'user', u'secret', u'User', u'*****@*****.**')
        ])
        self.store.commit()
        with login(u'user', objectID, self.transact) as session:
            deferred = self.facade.deleteUser(session, u'doomed')
            error = yield self.assertFailure(deferred, TPathPermissionDenied)
            self.assertEqual(u'doomed', error.path)