def finish(self, auth_entity, state=None): self.state = util.decode_oauth_state(state) if not state: self.error( 'If you want to publish or preview, please approve the prompt.' ) return self.redirect('/') source = ndb.Key(urlsafe=self.state['source_key']).get() if auth_entity is None: self.error( 'If you want to publish or preview, please approve the prompt.' ) elif not auth_entity.is_authority_for(source.auth_entity): self.error('Please log into %s as %s to publish that page.' % (source.GR_CLASS.NAME, source.name)) else: result = self._run() if result and result.content: self.messages.add( 'Done! <a href="%s">Click here to view.</a>' % self.entity.published.get('url')) granary_message = self.entity.published.get('granary_message') if granary_message: self.messages.add(granary_message) # otherwise error() added an error message return self.redirect(source.bridgy_url(self))
def finish(self, auth_entity, state=None): source = util.maybe_add_or_delete_source(Mastodon, auth_entity, state) features = util.decode_oauth_state(state).get('feature', '').split(',') if set(features) != set(source.features): # override features with whatever we requested scopes for just now, since # scopes are per access token. background: # https://github.com/snarfed/bridgy/issues/1015 source.features = features source.put()
def finish(self, auth_entity, state=None): logging.debug('finish with %s, %s', auth_entity, state) source = self.maybe_add_or_delete_source(Flickr, auth_entity, state) feature = util.decode_oauth_state(state).get('feature') if source and feature == 'listen' and 'publish' in source.features: # we had signed up previously with publish, so we'll reauth to # avoid losing that permission logging.info('Restarting OAuth flow to get publish permissions.') source.features.remove('publish') source.put() return self.start_oauth_flow('publish')
def finish(self, auth_entity, state=None): source = util.maybe_add_or_delete_source(Twitter, auth_entity, state) feature = util.decode_oauth_state(state).get('feature') if source is not None and feature == 'listen' and 'publish' in source.features: # if we were already signed up for publish, we had a read/write token. # when we sign up for listen, we use x_auth_access_type=read to request # just read permissions, which *demotes* us to a read only token! ugh. # so, do the whole oauth flow again to get a read/write token. logger.info('Restarting OAuth flow to get publish permissions.') source.features.remove('publish') source.put() return self.start_oauth_flow('publish')
def finish(self, auth_entity, state=None): source = self.maybe_add_or_delete_source(Twitter, auth_entity, state) feature = util.decode_oauth_state(state).get('feature') if source is not None and feature == 'listen' and 'publish' in source.features: # if we were already signed up for publish, we had a read/write token. # when we sign up for listen, we use x_auth_access_type=read to request # just read permissions, which *demotes* us to a read only token! ugh. # so, do the whole oauth flow again to get a read/write token. logging.info('Restarting OAuth flow to get publish permissions.') source.features.remove('publish') source.put() return self.start_oauth_flow('publish')
def finish_oauth_flow(self, auth_entity, state): """Adds or deletes a :class:`FacebookPage`, or restarts OAuth to get publish permissions. Args: auth_entity: :class:`oauth_dropins.facebook.FacebookAuth` state: encoded state string """ if auth_entity is None: auth_entity_key = self.request.get('auth_entity_key') if auth_entity_key: auth_entity = ndb.Key(urlsafe=auth_entity_key).get() if state is None: state = self.request.get('state') state_obj = util.decode_oauth_state(state) if state else {} id = state_obj.get('id') or self.request.get('id') if id and auth_entity and id != auth_entity.key.id(): auth_entity = auth_entity.for_page(id) if auth_entity: auth_entity.put() source = self.maybe_add_or_delete_source(FacebookPage, auth_entity, state) # If we were already signed up for publish, we had an access token with publish # permissions. If we then go through the listen signup flow, we'll get a token # with just the listen permissions. In that case, do the whole OAuth flow again # to get a token with publish permissions again. feature = state_obj.get('feature') if source is not None and feature == 'listen' and 'publish' in source.features: logging.info('Restarting OAuth flow to get publish permissions.') source.features.remove('publish') source.put() start = util.oauth_starter(oauth_facebook.StartHandler, feature='publish', id=id) restart = start.to('/facebook/oauth_handler', scopes=PUBLISH_SCOPES) restart(self.request, self.response).post() # ask the user for their web site if we don't already have one. if source and not source.domains: self.redirect( '/edit-websites?' + urllib.parse.urlencode({ 'source_key': source.key.urlsafe(), }))
def finish_oauth_flow(self, auth_entity, state): """Adds or deletes a :class:`FacebookPage`, or restarts OAuth to get publish permissions. Args: auth_entity: :class:`oauth_dropins.facebook.FacebookAuth` state: encoded state string """ if auth_entity is None: auth_entity_key = self.request.get('auth_entity_key') if auth_entity_key: auth_entity = ndb.Key(urlsafe=auth_entity_key).get() if state is None: state = self.request.get('state') state_obj = util.decode_oauth_state(state) if state else {} id = state_obj.get('id') or self.request.get('id') if id and auth_entity and id != auth_entity.key.id(): auth_entity = auth_entity.for_page(id) if auth_entity: auth_entity.put() source = self.maybe_add_or_delete_source(FacebookPage, auth_entity, state) # If we were already signed up for publish, we had an access token with publish # permissions. If we then go through the listen signup flow, we'll get a token # with just the listen permissions. In that case, do the whole OAuth flow again # to get a token with publish permissions again. feature = state_obj.get('feature') if source is not None and feature == 'listen' and 'publish' in source.features: logging.info('Restarting OAuth flow to get publish permissions.') source.features.remove('publish') source.put() start = util.oauth_starter(oauth_facebook.StartHandler, feature='publish', id=id) restart = start.to('/facebook/oauth_handler', scopes=PUBLISH_SCOPES) restart(self.request, self.response).post() # ask the user for their web site if we don't already have one. if source and not source.domains: self.redirect('/edit-websites?' + urllib.urlencode({ 'source_key': source.key.urlsafe(), }))
def oauth_callback(): """OAuth callback handler. Both the add and delete flows have to share this because Blogger's oauth-dropin doesn't yet allow multiple callback handlers. :/ """ auth_entity = None auth_entity_str_key = request.values.get('auth_entity') if auth_entity_str_key: auth_entity = ndb.Key(urlsafe=auth_entity_str_key).get() if not auth_entity.blog_ids or not auth_entity.blog_hostnames: auth_entity = None if not auth_entity: flash("Couldn't fetch your blogs. Maybe you're not a Blogger user?") state = request.values.get('state') if not state: state = util.construct_state_param_for_add(feature='webmention') if not auth_entity: util.maybe_add_or_delete_source(Blogger, auth_entity, state) return vars = { 'action': '/blogger/add', 'state': state, 'operation': util.decode_oauth_state(state).get('operation'), 'auth_entity_key': auth_entity.key.urlsafe().decode(), 'blogs': [{ 'id': id, 'title': title, 'domain': host } for id, title, host in zip(auth_entity.blog_ids, auth_entity. blog_titles, auth_entity.blog_hostnames)], } logger.info(f'Rendering choose_blog.html with {vars}') return render_template('choose_blog.html', **vars)
def finish(self, auth_entity, state=None): id = util.decode_oauth_state(state).get('id') if auth_entity and json.loads(auth_entity.pages_json) and not id: # this user has FB page(s), and we don't know whether they want to sign # themselves up or one of their pages, so ask them. vars = { 'action': '/facebook/add', 'state': state, 'auth_entity_key': auth_entity.key.urlsafe(), 'choices': [json.loads(auth_entity.user_json)] + json.loads(auth_entity.pages_json), } logging.info('Rendering choose_facebook.html with %s', vars) self.response.headers['Content-Type'] = 'text/html' self.response.out.write( JINJA_ENV.get_template('choose_facebook.html').render(**vars)) return # this user has no FB page(s), or we know the one they want to sign up. self.finish_oauth_flow(auth_entity, state)
def finish(self, auth_entity, state=None): self.state = util.decode_oauth_state(state) if not state: self.error('If you want to publish or preview, please approve the prompt.') return self.redirect('/') source = ndb.Key(urlsafe=self.state['source_key']).get() if auth_entity is None: self.error('If you want to publish or preview, please approve the prompt.') elif not auth_entity.is_authority_for(source.auth_entity): self.error('Please log into %s as %s to publish that page.' % (source.GR_CLASS.NAME, source.name)) else: result = self._run() if result and result.content: self.messages.add('Done! <a href="%s">Click here to view.</a>' % self.entity.published.get('url')) granary_message = self.entity.published.get('granary_message') if granary_message: self.messages.add(granary_message) # otherwise error() added an error message return self.redirect(source.bridgy_url(self))
def get(self): parts = util.decode_oauth_state(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) 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().decode(), }) 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 if callback else source. bridgy_url(self) if source.features else '/')
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)
def get(self): parts = util.decode_oauth_state(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 '/')