Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Save OAuth tokens #2810

Merged
merged 5 commits into from
Oct 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions branch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
BEGIN;
ALTER TABLE elsewhere DROP COLUMN access_token,
DROP COLUMN refresh_token,
DROP COLUMN expires;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the data that's already in these columns? It looks like we're throwing it away, yes? Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because keeping it is not worth the effort, it only exists in 59 rows (Venmo accounts), and I think it might be obsolete anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. We've been using an OAuth app for Venmo that is attached to my personal account. Due to bugs on their end we were unable to set up an app under our Gratipay account, but I believe that's resolved now. What are the implications of throwing away these access/refresh tokens? Does it make sense to also switch to a different Venmo OAuth app along with this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if we want to change some OAuth keys now is the time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll look into it.

ALTER TABLE elsewhere ADD COLUMN token json;

DROP TYPE elsewhere_with_participant CASCADE;
CREATE TYPE elsewhere_with_participant AS
( id integer
, platform text
, user_id text
, user_name text
, display_name text
, email text
, avatar_url text
, extra_info json
, is_locked boolean
, is_team boolean
, token json
, participant participants
); -- If Postgres had type inheritance this would be even awesomer.

CREATE OR REPLACE FUNCTION load_participant_for_elsewhere (elsewhere)
RETURNS elsewhere_with_participant
AS $$
SELECT $1.id
, $1.platform
, $1.user_id
, $1.user_name
, $1.display_name
, $1.email
, $1.avatar_url
, $1.extra_info
, $1.is_locked
, $1.is_team
, $1.token
, participants.*::participants
FROM participants
WHERE participants.username = $1.participant
;
$$ LANGUAGE SQL;

CREATE CAST (elsewhere AS elsewhere_with_participant)
WITH FUNCTION load_participant_for_elsewhere(elsewhere);

END;
6 changes: 3 additions & 3 deletions defaults.env
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ BOUNTYSOURCE_CALLBACK=http://127.0.0.1:8537/on/bountysource/associate
BOUNTYSOURCE_API_HOST=https://staging-api.bountysource.com
BOUNTYSOURCE_WWW_HOST=https://staging.bountysource.com

VENMO_CLIENT_ID=1534
VENMO_CLIENT_SECRET=55ckgsguYC3cj7xWW5c95PHvUzrwgZMA
VENMO_CALLBACK=http://127.0.0.1:8537/on/venmo/associate
VENMO_CLIENT_ID=2031
VENMO_CLIENT_SECRET=YaBbmZQVmQjfgL7NFekpyJv4PayUhwhb
VENMO_CALLBACK=http://localhost:8537/on/venmo/associate

OPENSTREETMAP_CONSUMER_KEY=J2SS5GM0A7tM1CIBjAHXUTMeCEkRBMYsTJzGONxe
OPENSTREETMAP_CONSUMER_SECRET=hgvZkbtWVOEoaJV5AzQPcBI9m8f7BylkpT0cP7wS
Expand Down
23 changes: 17 additions & 6 deletions gratipay/elsewhere/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from collections import OrderedDict
from datetime import datetime
import hashlib
import json
import logging
from urllib import quote
import xml.etree.ElementTree as ET
Expand Down Expand Up @@ -207,7 +208,11 @@ def get_user_self_info(self, sess):
"""Get the authenticated user's info from the API.
"""
r = self.api_get(self.api_user_self_info_path, sess=sess)
return self.extract_user_info(self.api_parser(r))
info = self.extract_user_info(self.api_parser(r))
token = getattr(sess, 'token', None)
if token:
info.token = json.dumps(token)
return info


class PlatformOAuth1(Platform):
Expand All @@ -216,8 +221,11 @@ class PlatformOAuth1(Platform):
authorize_path = '/oauth/authorize'
access_token_path = '/oauth/access_token'

def get_auth_session(self, token=None, token_secret=None):
return OAuth1Session(self.api_key, self.api_secret, token, token_secret,
def get_auth_session(self, token=None):
args = ()
if token:
args = (token['token'], token['token_secret'])
return OAuth1Session(self.api_key, self.api_secret, *args,
callback_uri=self.callback_url)

def get_auth_url(self, **kw):
Expand All @@ -230,9 +238,11 @@ def get_query_id(self, querystring):
return querystring['oauth_token']

def handle_auth_callback(self, url, token, token_secret):
sess = self.get_auth_session(token=token, token_secret=token_secret)
sess = self.get_auth_session(dict(token=token, token_secret=token_secret))
sess.parse_authorization_response(url)
sess.fetch_access_token(self.auth_url+self.access_token_path)
r = sess.fetch_access_token(self.auth_url+self.access_token_path)
sess.token = dict(token=r['oauth_token'],
token_secret=r['oauth_token_secret'])
return sess


Expand All @@ -242,8 +252,9 @@ class PlatformOAuth2(Platform):
oauth_email_scope = None
oauth_payment_scope = None

def get_auth_session(self, state=None, token=None):
def get_auth_session(self, state=None, token=None, token_updater=None):
return OAuth2Session(self.api_key, state=state, token=token,
token_updater=token_updater,
redirect_uri=self.callback_url,
scope=self.oauth_default_scope)

Expand Down
19 changes: 12 additions & 7 deletions gratipay/models/account_elsewhere.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ def upsert(cls, i):
# Random Stuff
# ============

def get_auth_session(self):
if not self.token:
return
params = dict(token=self.token)
if 'refresh_token' in self.token:
params['token_updater'] = self.save_token
return self.platform_data.get_auth_session(**params)

@property
def html_url(self):
return self.platform_data.account_url.format(
Expand Down Expand Up @@ -157,18 +165,15 @@ def opt_in(self, desired_username):
user.participant.update_is_closed(False)
return user, newly_claimed

def save_token(self, token, refresh_token=None, expires=None):
def save_token(self, token):
"""Saves the given access token in the database.
"""
self.db.run("""
UPDATE elsewhere
SET (access_token, refresh_token, expires) = (%s, %s, %s)
SET token = %s
WHERE id=%s
""", (token, refresh_token, expires, self.id))
self.set_attributes( access_token=token
, refresh_token=refresh_token
, expires=expires
)
""", (token, self.id))
self.set_attributes(token=token)

def set_is_locked(self, is_locked):
self.db.run( 'UPDATE elsewhere SET is_locked=%s WHERE id=%s'
Expand Down
21 changes: 21 additions & 0 deletions www/%username/elsewhere/refresh.spt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from aspen import Response
from gratipay.models.account_elsewhere import AccountElsewhere
from gratipay.utils import get_participant

[---]

if not user.ADMIN:
raise Response(403)

participant = get_participant(request, restrict=True)
accounts = participant.get_accounts_elsewhere()
i = 0
for account in accounts.values():
sess = account.get_auth_session()
if not sess:
continue
AccountElsewhere.upsert(account.platform_data.get_user_self_info(sess))
i += 1

[---] text/html
Updated {{i}} accounts.