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))
def testVerifyCredentialsUnpacksSuccessfulResponse(self): """ L{ServiceProvider.verifyCredentials} returns a C{Deferred} that fires with the user object returned by Twitter, as a C{dict}, when credentials are successfully verified. """ self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') deferred = provider.verifyCredentials(authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), dumps({'id': 1984245})) responseDeferred.callback(response) user = yield deferred self.assertEqual(1984245, user['id'])
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)
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)
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)
def testVerifyCredentialsWithUnsuccessfulResponseCode(self): """ L{ServiceProvider.verifyCredentials} fires an errback with a L{ServiceProviderError} exception if the service provider returns a code other than C{200 OK}. The exception contains the HTTP status code and the payload, if any, that was sent by the service provider. """ self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') deferred = provider.verifyCredentials(authentication) [(request, responseDeferred)] = self.protocol.requests response = FakeResponse(ResponseDone(), 'Something bad happened', code=BAD_REQUEST) responseDeferred.callback(response) error = yield self.assertFailure(deferred, ServiceProviderError) self.assertEqual(BAD_REQUEST, error.code) self.assertEqual('Something bad happened', error.payload)
def testVerifyCredentialsPassesOAuthEchoHeadersWithRequest(self): """ L{ServiceProvider.verifyCredentials} makes a request to Twitter to verify credentials for the consumer, as part of the OAuth Echo process. It includes an C{Authentication} response header with the OAuth Echo credentials provided by the consumer. """ self.agent._connect = self._connect authentication = 'OAuth oauth_consumer_key="...", ...' provider = ServiceProvider(self.agent, 'https://example.com/verify') provider.verifyCredentials(authentication) [(request, responseDeferred)] = self.protocol.requests self.assertIsInstance(request, Request) self.assertEqual('GET', request.method) self.assertEqual('/verify', request.uri) self.assertEqual(Headers({'Authorization': [authentication], 'Host': ['example.com']}), request.headers)
def render_GET(self, request): """Handle an OAuth Echo request. Call the original service provider's endpoint to verify credentials (at least that's what Twitter calls it) and return the result, along with appropriate Fluidinfo-specific headers. The client invoking this method is the consumer, Fluidinfo is the delegator and Twitter is the service provider, in the OAuth Echo process. A C{deferred} attribute is set on the instance for testing purposes. It will fire with the result of a call to L{Delegator.getUser}. @param request: The HTTP request. @return: Either an empty string (in the case of an error), or C{NOT_DONE_YET} to indicate processing is ongoing. """ def succeeded(result): """ Handle successful credentials verification with the L{ServiceProvider}. """ # FIXME UTF-8 encode and base64 the username. username = result['username'] if result['new-user']: request.setHeader('X-FluidDB-New-User', 'true') if result['missing-password']: request.setHeader('X-FluidDB-Missing-Password', 'true') encodedUsername = b64encode(username.encode('utf-8')) request.setHeader('X-FluidDB-Username', encodedUsername) request.setHeader('X-FluidDB-Access-Token', result['access-token']) request.setHeader('X-FluidDB-Renewal-Token', result['renewal-token']) request.write(dumps(result['data'])) request.setResponseCode(OK) request.finish() return result def failed(failure): """ Handle a failure that occurred while attempting to verify credentials with the L{ServiceProvider}. """ if failure.check(DuplicateUserError): logging.warning('Could not authenticate duplicate user:'******'utf-8')) request.setHeader('X-FluidDB-Username', encodedUsername) request.setHeader('X-FluidDB-Error-Class', 'UsernameConflict') request.setResponseCode(CONFLICT) else: logging.warning('Could not complete OAuth Echo request:') logging.exception(failure.value) request.setResponseCode(SERVICE_UNAVAILABLE) request.setHeader('X-FluidDB-Request-Id', self._session.id) request.finish() def crashed(failure): """ Handle an unexpected failure that occurred while attempting to handle this request. """ logging.error('Unexpected error handling OAuth Echo request:') logging.exception(failure.value) request.setResponseCode(INTERNAL_SERVER_ERROR) request.finish() try: url, authorization = self._getParameters(request) except OAuthEchoRequestError as error: request.setResponseCode(BAD_REQUEST) request.setHeader('X-FluidDB-Request-Id', self._session.id) request.setHeader('X-FluidDB-Error-Class', error.errorClass) if error.badHeader: request.setHeader('X-FluidDB-Header', error.badHeader) return '' else: consumerUsername = self._session.auth.username provider = ServiceProvider(self._agent, url) delegator = Delegator(self._session.transact) self.deferred = delegator.getUser(consumerUsername, provider, authorization) self.deferred.addCallbacks(succeeded, failed) self.deferred.addErrback(crashed) return NOT_DONE_YET