def get(self): parts = self.decode_state_parameter(util.get_required_param(self, 'state')) callback = parts and parts.get('callback') if self.request.get('declined'): if callback: # disable declined means no change took place callback = util.add_query_params(callback, {'result': 'declined'}) else: self.messages.add('If you want to disable, please approve the prompt.') self.redirect(callback.encode('utf-8') if callback else '/') return if (not parts or 'feature' not in parts or 'source' not in parts): self.abort(400, 'state query parameter must include "feature" and "source"') feature = parts['feature'] if feature not in (Source.FEATURES): self.abort(400, 'cannot delete unknown feature %s' % feature) logged_in_as = ndb.Key( urlsafe=util.get_required_param(self, 'auth_entity')).get() source = ndb.Key(urlsafe=parts['source']).get() if logged_in_as and logged_in_as.is_authority_for(source.auth_entity): # TODO: remove credentials if feature in source.features: source.features.remove(feature) source.put() noun = 'webmentions' if feature == 'webmention' else feature + 'ing' if callback: callback = util.add_query_params(callback, { 'result': 'success', 'user': source.bridgy_url(self), 'key': source.key.urlsafe(), }) else: self.messages.add('Disabled %s for %s. Sorry to see you go!' % (noun, source.label())) # util.email_me(subject='Deleted Bridgy %s user: %s %s' % # (feature, source.label(), source.key.string_id()), # body=source.bridgy_url(self)) else: if callback: callback = util.add_query_params(callback, {'result': 'failure'}) else: self.messages.add('Please log into %s as %s to disable it here.' % (source.GR_CLASS.NAME, source.name)) self.redirect(callback.encode('utf-8') if callback else source.bridgy_url(self) if source.features else '/')
def new(handler, auth_entity=None, **kwargs): """Creates and returns a GooglePlusPage for the logged in user. Args: handler: the current RequestHandler auth_entity: oauth_dropins.googleplus.GooglePlusAuth """ # Google+ Person resource # https://developers.google.com/+/api/latest/people#resource user = json.loads(auth_entity.user_json) type = 'user' if user.get('objectType', 'person') == 'person' else 'page' # override the sz param to ask for a 128x128 image. if there's an existing # sz query param (there usually is), the new one will come afterward and # override it. picture = user.get('image', {}).get('url') picture = util.add_query_params(picture, {'sz': '128'}) return GooglePlusPage(id=user['id'], auth_entity=auth_entity.key, url=user.get('url'), name=user.get('displayName'), picture=picture, type=type, **kwargs)
def new(handler, auth_entity=None, **kwargs): """Creates and returns a GooglePlusPage for the logged in user. Args: handler: the current RequestHandler auth_entity: oauth_dropins.googleplus.GooglePlusAuth """ # Google+ Person resource # https://developers.google.com/+/api/latest/people#resource user = json.loads(auth_entity.user_json) type = "user" if user.get("objectType", "person") == "person" else "page" # override the sz param to ask for a 128x128 image. if there's an existing # sz query param (there usually is), the new one will come afterward and # override it. picture = user.get("image", {}).get("url") picture = util.add_query_params(picture, {"sz": "128"}) return GooglePlusPage( id=user["id"], auth_entity=auth_entity.key, url=user.get("url"), name=user.get("displayName"), picture=picture, type=type, **kwargs )
def new(handler, auth_entity=None, **kwargs): """Creates and returns a :class:`GooglePlusPage` for the logged in user. Args: handler: the current :class:`webapp2.RequestHandler` auth_entity: :class:`oauth_dropins.googleplus.GooglePlusAuth` """ # Google+ Person resource # https://developers.google.com/+/api/latest/people#resource user = json.loads(auth_entity.user_json) type = 'user' if user.get('objectType', 'person') == 'person' else 'page' # override the sz param to ask for a 128x128 image. if there's an existing # sz query param (there usually is), the new one will come afterward and # override it. picture = user.get('image', {}).get('url') picture = util.add_query_params(picture, {'sz': '128'}) return GooglePlusPage(id=user['id'], auth_entity=auth_entity.key, url=user.get('url'), name=user.get('displayName'), picture=picture, type=type, **kwargs)
def finish(self, auth_entity, state=None): if 'target_url' in self.decode_state_parameter(state): # this is an interactive publish return self.redirect(util.add_query_params( '/publish/instagram/finish', util.trim_nulls({'auth_entity': auth_entity.key.urlsafe(), 'state': state}))) self.maybe_add_or_delete_source(Instagram, auth_entity, state)
def test_add_query_param(self): for expected, url, params in ( ('http://a.com?x=', 'http://a.com', [('x', '')]), ('http://a.com?x=y', 'http://a.com', [('x', 'y')]), ('http://a.com?x=y&u=v', 'http://a.com', [('x', 'y'), ('u', 'v')]), ('http://a.com?x=y&u=v', 'http://a.com?x=y', [('u', 'v')]), ('http://a.com?x=y&u=v', 'http://a.com?x=y', [('u', 'v')]), ('http://a.com?x=y&x=z', 'http://a.com', [('x', 'y'), ('x', 'z')]), ('http://a.com?x=y&x=z&x=w', 'http://a.com?x=y&x=z', [('x', 'w')]), ('http://a.com?x=y', 'http://a.com', {'x': 'y'}), # note encoding declaration at top of file ('http://a.com?x=R+%C3%87', 'http://a.com', {'x': u'R Ç'}), ('http://a.com?x=R+%C3%87&x=R+%C3%87', 'http://a.com?x=R+%C3%87', {'x': u'R Ç'}), ): self.assertEqual(expected, util.add_query_params(url, params)) for expected, req, params in ( (urllib2.Request('http://a.com?x=y'), urllib2.Request('http://a.com'), [('x', 'y')]), (urllib2.Request('http://a.com?x=y&u=v'), urllib2.Request('http://a.com?x=y'), [('u', 'v')]), (urllib2.Request('http://a.com?x=y', data='my data', headers={'X': 'Y'}), urllib2.Request('http://a.com', data='my data', headers={'X': 'Y'}), [('x', 'y')]), ): actual = util.add_query_params(req, params) self.assertIsInstance(actual, urllib2.Request) self.assertEqual(expected.get_full_url(), actual.get_full_url()) self.assertEqual(expected.get_data(), actual.get_data()) self.assertEqual(expected.headers, actual.headers) query_string = '' for i in range(2): query_string = util.add_query_params(query_string, {'x': u'Ryan Çelik'}) for key, val in urlparse.parse_qsl(query_string[1:]): self.assertEquals('x', key) self.assertEquals(u'Ryan Çelik', val.decode('utf-8'))
def get(self, feature): """Redirect to the front page.""" self.redirect(util.add_query_params('/', self.request.params.items()), permanent=True)
def get(self): parts = self.decode_state_parameter(self.request.get('state') or '') callback = parts and parts.get('callback') if self.request.get('declined'): # disable declined means no change took place if callback: callback = util.add_query_params(callback, {'result': 'declined'}) self.redirect(callback.encode('utf-8')) else: self.messages.add('If you want to disable, please approve the prompt.') self.redirect('/') return if (not parts or 'feature' not in parts or 'source' not in parts): self.abort(400, 'state query parameter must include "feature" and "source"') feature = parts['feature'] if feature not in (Source.FEATURES): self.abort(400, 'cannot delete unknown feature %s' % feature) logged_in_as = ndb.Key( urlsafe=util.get_required_param(self, 'auth_entity')).get() source = ndb.Key(urlsafe=parts['source']).get() if logged_in_as and logged_in_as.is_authority_for(source.auth_entity): # TODO: remove credentials if feature in source.features: source.features.remove(feature) source.put() # remove login cookie logins = self.get_logins() login = util.Login(path=source.bridgy_path(), site=source.SHORT_NAME, name=source.label_name()) if login in logins: logins.remove(login) self.set_logins(logins) noun = 'webmentions' if feature == 'webmention' else feature + 'ing' if callback: callback = util.add_query_params(callback, { 'result': 'success', 'user': source.bridgy_url(self), 'key': source.key.urlsafe(), }) else: self.messages.add('Disabled %s for %s. Sorry to see you go!' % (noun, source.label())) # util.email_me(subject='Deleted Bridgy %s user: %s %s' % # (feature, source.label(), source.key.string_id()), # body=source.bridgy_url(self)) else: if callback: callback = util.add_query_params(callback, {'result': 'failure'}) else: self.messages.add('Please log into %s as %s to disable it here.' % (source.GR_CLASS.NAME, source.name)) self.redirect(callback.encode('utf-8') if callback else source.bridgy_url(self) if source.features else '/')
def test_live(self): # sign up (use the form inputs in our actual HTML template) with open('templates/facebook_signup.html') as f: resp = self.submit_form(f.read()) self.assertEqual(302, resp.status_int) to = resp.headers['Location'] self.assertTrue(to.startswith('https://www.facebook.com/v2.6/dialog/oauth?'), to) redirect = urlparse.parse_qs(urlparse.urlparse(to).query)['redirect_uri'][0] self.dot() # pretend the user approves the prompt and facebook redirects back to us. # mock out the access token request since we use a canned token. self.expect_urlopen(oauth_facebook.GET_ACCESS_TOKEN_URL % { 'client_id': appengine_config.FACEBOOK_APP_ID, 'client_secret': appengine_config.FACEBOOK_APP_SECRET, 'redirect_uri': urllib.quote_plus(redirect), 'auth_code': 'fake_code', }, '{"access_token": "%s"}' % appengine_config.FACEBOOK_TEST_USER_TOKEN, ).WithSideEffects(lambda *args, **kwargs: self.mox.stubs.UnsetAll()) self.mox.ReplayAll() resp = facebook.application.get_response( util.add_query_params(redirect, {'code': 'fake_code'})) self.assertEqual(302, resp.status_int) source = facebook.FacebookPage.get_by_id(TEST_USER_ID) self.assertEqual('enabled', source.status) self.assertEqual(['listen'], source.features) self.dot() # ignore all domains except example.zz util.in_webmention_blacklist = lambda domain: domain != 'example.zz' # poll self.stub_requests_head() resp = self.run_task(self.taskqueue_stub.GetTasks('poll')[0]) self.assertEqual(200, resp.status_int) self.dot() # three propagates, one for the like and one for each comment source_urls = [] def handle_post_body(params): self.assertEqual('http://example.zz/abc', params['target']) source_urls.append(params['source']) return True self.mox.StubOutWithMock(requests, 'post', use_mock_anything=True) self.expect_requests_post( 'http://example.zz/wm', timeout=mox.IgnoreArg(), verify=mox.IgnoreArg(), data=mox.Func(handle_post_body) ).MultipleTimes() self.mox.ReplayAll() memcache.set('W http example.zz', 'http://example.zz/wm') for task in self.taskqueue_stub.GetTasks('propagate'): resp = self.run_task(task) self.assertEqual(200, resp.status_int) self.mox.stubs.UnsetAll() self.dot() # fetch the response handler URLs for url in source_urls: resp = handlers.application.get_response(url) self.assertEqual(200, resp.status_int) self.dot()
def test_live(self): # sign up (use the form inputs in our actual HTML template) with open('templates/facebook_signup.html') as f: resp = self.submit_form(f.read()) self.assertEqual(302, resp.status_int) to = resp.headers['Location'] self.assertTrue(to.startswith('https://www.facebook.com/v2.10/dialog/oauth?'), to) params = urlparse.parse_qs(urlparse.urlparse(to).query) redirect = params['redirect_uri'][0] state = params['state'][0] self.dot() # pretend the user approves the prompt and facebook redirects back to us. # mock out the access token request since we use a canned token. self.expect_urlopen(oauth_facebook.GET_ACCESS_TOKEN_URL % { 'client_id': appengine_config.FACEBOOK_APP_ID, 'client_secret': appengine_config.FACEBOOK_APP_SECRET, 'redirect_uri': urllib.quote_plus(redirect), 'auth_code': 'fake_code', }, '{"access_token": "%s"}' % appengine_config.FACEBOOK_TEST_USER_TOKEN, ).WithSideEffects(lambda *args, **kwargs: self.mox.stubs.UnsetAll()) self.mox.ReplayAll() resp = facebook.application.get_response( util.add_query_params(redirect, { 'code': 'fake_code', 'state': urllib.unquote(state), })) self.assertEqual(302, resp.status_int) source = facebook.FacebookPage.get_by_id(TEST_USER_ID) self.assertEqual('enabled', source.status) self.assertEqual(['listen'], source.features) self.dot() # ignore all domains except example.zz util.in_webmention_blacklist = lambda domain: domain != 'example.zz' # poll self.stub_requests_head() resp = self.run_task(self.taskqueue_stub.GetTasks('poll')[0]) self.assertEqual(200, resp.status_int) self.dot() # three propagates, one for the like and one for each comment source_urls = [] def handle_post_body(params): self.assertEqual('http://example.zz/abc', params['target']) source_urls.append(params['source']) return True self.mox.StubOutWithMock(requests, 'post', use_mock_anything=True) self.expect_requests_post( 'http://example.zz/wm', timeout=mox.IgnoreArg(), verify=mox.IgnoreArg(), data=mox.Func(handle_post_body), allow_redirects=False, headers={'Accept': '*/*'}).MultipleTimes() self.mox.ReplayAll() memcache.set('W http example.zz', 'http://example.zz/wm') for task in self.taskqueue_stub.GetTasks('propagate'): resp = self.run_task(task) self.assertEqual(200, resp.status_int) self.mox.stubs.UnsetAll() self.dot() # fetch the response handler URLs for url in source_urls: resp = handlers.application.get_response(url) self.assertEqual(200, resp.status_int) self.dot()
def redirect_to_front_page(_): """Redirect to the front page.""" return redirect(util.add_query_params('/', request.values.items()), code=301)
def delete_finish(): parts = util.decode_oauth_state(request.values.get('state') or '') callback = parts and parts.get('callback') if request.values.get('declined'): # disable declined means no change took place if callback: callback = util.add_query_params(callback, {'result': 'declined'}) return redirect(callback) else: flash('If you want to disable, please approve the prompt.') return redirect('/') return if not parts or 'feature' not in parts or 'source' not in parts: error('state query parameter must include "feature" and "source"') feature = parts['feature'] if feature not in (Source.FEATURES): error(f'cannot delete unknown feature {feature}') logged_in_as = ndb.Key(urlsafe=request.args['auth_entity']).get() source = ndb.Key(urlsafe=parts['source']).get() logins = None if logged_in_as and logged_in_as.is_authority_for(source.auth_entity): # TODO: remove credentials if feature in source.features: source.features.remove(feature) source.put() # remove login cookie logins = util.get_logins() login = util.Login(path=source.bridgy_path(), site=source.SHORT_NAME, name=source.label_name()) if login in logins: logins.remove(login) if callback: callback = util.add_query_params( callback, { 'result': 'success', 'user': source.bridgy_url(), 'key': source.key.urlsafe().decode(), }) else: nouns = { 'webmention': 'webmentions', 'listen': 'backfeed', 'publish': 'publishing', } msg = f'Disabled {nouns[feature]} for {source.label()}.' if not source.features: msg += ' Sorry to see you go!' flash(msg) elif callback: callback = util.add_query_params(callback, {'result': 'failure'}) else: flash( f'Please log into {source.GR_CLASS.NAME} as {source.name} to disable it here.' ) url = callback if callback else source.bridgy_url( ) if source.features else '/' return redirect(url, logins=logins)