Ejemplo n.º 1
0
    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))
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
 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')
Ejemplo n.º 4
0
 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')
Ejemplo n.º 5
0
  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')
Ejemplo n.º 6
0
  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')
Ejemplo n.º 7
0
    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(),
                }))
Ejemplo n.º 8
0
  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(),
      }))
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
  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)
Ejemplo n.º 11
0
  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))
Ejemplo n.º 12
0
    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 '/')
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
  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 '/')