From 6cedb7b5dbee654c8c556967288ec51fb809dd64 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 26 Jan 2021 20:26:51 +0100 Subject: [PATCH] rp3 auth working --- rowers/rp3stuff.py | 16 ++++++------ rowers/templates/rower_exportsettings.html | 9 +++++-- rowers/urls.py | 1 + rowers/views/importviews.py | 28 ++++++++++++++++++--- rowers/views/statements.py | 2 ++ static/img/logo-rp3-full-black.png | Bin 0 -> 14279 bytes 6 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 static/img/logo-rp3-full-black.png diff --git a/rowers/rp3stuff.py b/rowers/rp3stuff.py index 233e059d..02569b48 100644 --- a/rowers/rp3stuff.py +++ b/rowers/rp3stuff.py @@ -16,8 +16,8 @@ from io import BytesIO from rowsandall_app.settings import ( C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET, - rp3_CLIENT_ID, rp3_CLIENT_SECRET, - rp3_REDIRECT_URI,rp3_CLIENT_KEY, + RP3_CLIENT_ID, RP3_CLIENT_SECRET, + RP3_REDIRECT_URI,RP3_CLIENT_KEY, RP3_CLIENT_ID, RP3_CLIENT_KEY, RP3_REDIRECT_URI, RP3_CLIENT_SECRET ) @@ -56,23 +56,25 @@ def do_refresh_token(refreshtoken): # Exchange access code for long-lived access token def get_token(code): - client_auth = requests.auth.HTTPBasicAuth(rp3_CLIENT_KEY, rp3_CLIENT_SECRET) + client_auth = requests.auth.HTTPBasicAuth(RP3_CLIENT_KEY, RP3_CLIENT_SECRET) post_data = { - "client_id":rp3_CLIENT_KEY, + "client_id":RP3_CLIENT_KEY, "grant_type": "authorization_code", "code": code, - "redirect_uri":rp3_REDIRECT_URI, - "client_secret": rp3_CLIENT_SECRET, + "redirect_uri":RP3_REDIRECT_URI, + "client_secret": RP3_CLIENT_SECRET, } headers = { 'Content-Type': 'application/x-www-form-urlencoded', } response = requests.post( - "https://oauth.trainingpeaks.com/oauth/token", + "https://rp3rowing-app.com/oauth/token", data=post_data,verify=False, ) + print(response.json()) + try: token_json = response.json() diff --git a/rowers/templates/rower_exportsettings.html b/rowers/templates/rower_exportsettings.html index 25bfe659..81179cb4 100644 --- a/rowers/templates/rower_exportsettings.html +++ b/rowers/templates/rower_exportsettings.html @@ -30,7 +30,10 @@ Strava, {% endif %} {% if rower.runkeepertoken is not None and rower.runkeepertoken != '' %} - Runkeeper. + Runkeeper, + {% endif %} + {% if rower.rp3token is not None and rower.rp3token != '' %} + RP3 {% endif %}

@@ -77,7 +80,9 @@ alt="connect with Polar" width="130">

connect with Garmin

+ alt="connect with Garmin" width="130">

+

connect with RP3

{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index 3314730a..d33b5393 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -649,6 +649,7 @@ urlpatterns = [ re_path(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize,name='rower_sporttracks_authorize'), re_path(r'^me/underarmourauthorize/$',views.rower_underarmour_authorize,name='rower_underarmour_authorize'), re_path(r'^me/tpauthorize/$',views.rower_tp_authorize,name='rower_tp_authorize'), + re_path(r'^me/rp3authorize/$',views.rower_rp3_authorize,name='rower_rp3_authorize'), re_path(r'^me/runkeeperauthorize/$',views.rower_runkeeper_authorize,name='rower_runkeeper_authorize'), re_path(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh,name='rower_sporttracks_token_refresh'), re_path(r'^me/underarmourrefresh/$',views.rower_underarmour_token_refresh,name='rower_underarmoud_token_refresh'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index e7fce5cd..de8994f8 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -497,6 +497,21 @@ def rower_underarmour_authorize(request): return HttpResponseRedirect(url) +# Underarmour Authorization +@login_required() +def rower_rp3_authorize(request): + # Generate a random string for the state parameter + # Save it for use later to prevent xsrf attacks + + state = str(uuid4()) + params = {"client_id": RP3_CLIENT_KEY, + "response_type": "code", + "redirect_uri": RP3_REDIRECT_URI, + } + url = "https://rp3rowing-app.com/oauth/authorize/?" +urllib.parse.urlencode(params) + + return HttpResponseRedirect(url) + # Underarmour Authorization @login_required() def rower_tp_authorize(request): @@ -874,7 +889,7 @@ def rower_process_rp3callback(request): url = reverse('rower_exportsettings_view') return HttpResponseRedirect(url) - res = tpstuff.get_token(code) + res = rp3stuff.get_token(code) access_token = res[0] expires_in = res[1] @@ -882,12 +897,17 @@ def rower_process_rp3callback(request): expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in) r = getrower(request.user) - r.tptoken = access_token - r.tptokenexpirydate = expirydatetime - r.tprefreshtoken = refresh_token + r.rp3token = access_token + r.rp3tokenexpirydate = expirydatetime + r.rp3refreshtoken = refresh_token r.save() + successmessage = "Tokens stored. Good to go. Please check your import/export settings" + messages.info(request,successmessage) + url = reverse('rower_exportsettings_view') + return HttpResponseRedirect(url) + # Process TrainingPeaks callback @login_required() def rower_process_tpcallback(request): diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 409dab68..f6eb7cdb 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -167,6 +167,7 @@ import rowers.underarmourstuff as underarmourstuff from rowers.underarmourstuff import underarmour_open import rowers.tpstuff as tpstuff import rowers.runkeeperstuff as runkeeperstuff +import rowers.rp3stuff as rp3stuff import rowers.ownapistuff as ownapistuff from rowers.ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI from rowsandall_app.settings import ( @@ -179,6 +180,7 @@ from rowsandall_app.settings import ( UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY, RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET, TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET, + RP3_CLIENT_ID,RP3_REDIRECT_URI,RP3_CLIENT_KEY,RP3_CLIENT_SECRET, BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY, PAYMENT_PROCESSING_ON, RECAPTCHA_SITE_KEY, RECAPTCHA_SITE_SECRET diff --git a/static/img/logo-rp3-full-black.png b/static/img/logo-rp3-full-black.png new file mode 100644 index 0000000000000000000000000000000000000000..389d083c4aa11b9c1eb7ba8de34e779e16811bc8 GIT binary patch literal 14279 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 literal 0 HcmV?d00001