From e80527c688ae570205a793f2778cdc784692033d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 10 Jun 2021 08:53:35 +0200 Subject: [PATCH 1/3] solving c2 imports, forbidding free coaches access to imports --- rowers/rower_rules.py | 5 +++++ rowers/tests/test_imports.py | 20 ++++++++++++++++++++ rowers/urls.py | 2 ++ rowers/views/importviews.py | 36 ++++++++++++++++++++++++++++++++++-- rowers/views/statements.py | 2 +- rowers/views/workoutviews.py | 3 +++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/rowers/rower_rules.py b/rowers/rower_rules.py index 757e23e4..02920408 100644 --- a/rowers/rower_rules.py +++ b/rowers/rower_rules.py @@ -110,6 +110,10 @@ def is_staff(user): # pragma: no cover def is_coach(user): return user.rower.rowerplan in ['coach','freecoach'] +@rules.predicate +def is_not_freecoach(user): + return user.rower.rowerplan != 'freecoach' + def is_paid_coach(user): return user.rower.rowerplan == 'coach' @@ -324,6 +328,7 @@ rules.add_perm('rower.add_plan',can_plan_user) # replaces checkaccessplanuser rules.add_perm('rower.is_coach',is_coach_user) # replaces checkaccessuser rules.add_perm('rower.is_pro',ispromember) rules.add_perm('rower.is_staff',is_staff) +rules.add_perm('rower.is_not_freecoach',is_not_freecoach) # WORKOUT permissions diff --git a/rowers/tests/test_imports.py b/rowers/tests/test_imports.py index d81d07e7..81688f12 100644 --- a/rowers/tests/test_imports.py +++ b/rowers/tests/test_imports.py @@ -373,6 +373,26 @@ class C2Objects(DjangoTestCase): self.assertEqual(got, want) self.assertEqual(workoutdate,'2021-05-26') + def test_c2_import_54838157(self): + with open('rowers/tests/testdata/c2_54838157.json','r') as infile: + data = json.load(infile) + ( + startdatetime, + starttime, + workoutdate, + duration, + starttimeunix, + timezone + ) = utils.get_startdatetime_from_c2data(data) + + + self.assertEqual(str(timezone),'America/Los_Angeles') + + got = arrow.get(startdatetime).isoformat() + want = arrow.get('2021-06-06 17:14:41.400000-07:00').isoformat() + + self.assertEqual(got, want) + self.assertEqual(workoutdate,'2021-06-06') @patch('rowers.c2stuff.requests.get', side_effect=mocked_requests) diff --git a/rowers/urls.py b/rowers/urls.py index 2b5d1cf2..c308e6a4 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -497,11 +497,13 @@ urlpatterns = [ re_path(r'^workout/c2import/all/(?P\d+)/$',views.workout_getc2workout_all,name='workout_getc2workout_all'), re_path(r'^workout/nkimport/$',views.workout_nkimport_view,name='workout_nkimport_view'), re_path(r'^workout/nkimport/(?P\d+)/(?P\d+)/$',views.workout_nkimport_view,name='workout_nkimport_view'), + re_path(r'^workout/nkimport/user/(?P\d+)/$',views.workout_nkimport_view,name='workout_nkimport_view'), re_path(r'^workout/nkimport/all/$',views.workout_getnkworkout_all,name='workout_getnkworkout_all'), re_path(r'^workout/nkimport/all/(?P\d+-\d+-\d+)/(?P\d+-\d+-\d+)/$',views.workout_getnkworkout_all, name='workout_getnkworkout_all'), re_path(r'^workout/rp3import/(?P\d+)/$',views.workout_getrp3importview, name='workout_getrp3importview'), + re_path(r'^workout/rp3import/user/(?P\d+)/$',views.workout_rp3import_view,name='workout_rp3import_view'), re_path(r'^workout/rp3import/all/$',views.workout_getrp3workout_all,name='workout_getrp3workout_all'), re_path(r'^workout/(?P\w+.*)import/(?P\d+)/$',views.workout_getimportview,name='workout_getimportview'), re_path(r'^workout/(?P\w+.*)import/(?P\d+)/async/$',views.workout_getimportview,{'do_async':True},name='workout_getimportview'), diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index ec78c966..85fdd29f 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -537,9 +537,16 @@ def workout_getnkworkout_all(request,startdatestring='',enddatestring=''): @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid, raise_exception=True) +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_nkimport_view(request,userid=0,after=0,before=0): startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='last30') r = getrequestrower(request,userid=userid) + if r.user != request.user: + print(r,r.user,request.user) + messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_nkimport_view',kwargs={'userid':request.user.id}) + return HttpResponseRedirect(url) + try: thetoken = nk_open(request.user) except NoTokenError: # pragma: no cover @@ -908,8 +915,14 @@ def workout_rp3import_view(request,userid=0): # The page where you select which Strava workout to import @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_stravaimport_view(request,message="",userid=0): r = getrequestrower(request,userid=userid) + if r.user != request.user: + print(r,r.user,request.user) + messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_stravaimport_view',kwargs={'userid':request.user.id}) + return HttpResponseRedirect(url) #if r.user != request.user: # messages.info(request,"You cannot import other people's workouts from Strava") try: @@ -1260,6 +1273,7 @@ def garmin_details_view(request): # the page where you select which Polar workout to Import @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_polarimport_view(request,userid=0): # pragma: no cover exercises = polarstuff.get_polar_workouts(request.user) workouts = [] @@ -1318,8 +1332,14 @@ def workout_polarimport_view(request,userid=0): # pragma: no cover # The page where you select which SportTracks workout to import @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_sporttracksimport_view(request,message="",userid=0): - + r = getrequestrower(request,userid=userid) + if r.user != request.user: + print(r,r.user,request.user) + messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_sporttracksimport_view',kwargs={'userid':request.user.id}) + return HttpResponseRedirect(url) res = sporttracksstuff.get_sporttracks_workout_list(request.user) if (res.status_code != 200): @@ -1339,7 +1359,7 @@ def workout_sporttracksimport_view(request,message="",userid=0): return HttpResponseRedirect(url) workouts = [] - r = getrower(request.user) + stids = [int(getidfromuri(item['uri'])) for item in res.json()['items']] knownstids = uniqify([ w.uploadedtosporttracks for w in Workout.objects.filter(user=r) @@ -1469,9 +1489,15 @@ def workout_getrp3workout_all(request): # pragma: no cover # List of workouts available on Concept2 logbook - for import @login_required() @permission_required('rower.is_coach',fn=get_user_by_userid,raise_exception=True) +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_c2import_view(request,page=1,userid=0,message=""): r = getrequestrower(request,userid=userid) + if r.user != request.user: + print(r,r.user,request.user) + messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_c2import_view',kwargs={'userid':request.user.id}) + return HttpResponseRedirect(url) try: thetoken = c2_open(request.user) @@ -1581,8 +1607,14 @@ importsources = { } @login_required() +@permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_getrp3importview(request,externalid): r = getrequestrower(request) + if r.user != request.user: + print(r,r.user,request.user) + messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') + url = reverse('workout_rp3import_view',kwargs={'userid':request.user.id}) + return HttpResponseRedirect(url) token = rp3stuff.rp3_open(r.user) startdatetime = request.GET.get('startdatetime') diff --git a/rowers/views/statements.py b/rowers/views/statements.py index b3d39580..fec0ddb4 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -1194,7 +1194,7 @@ from rowers.utils import ( str2bool,range_to_color_hex,absolute,myqueue,get_call, calculate_age,rankingdistances,rankingdurations, my_dict_from_instance,wavg,NoTokenError, - request_is_ajax + request_is_ajax,dologging ) import rowers.datautils as datautils diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 34b50c72..2206b859 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4913,6 +4913,9 @@ def workout_upload_api(request): w.starttime = w.startdatetime.strftime('%H:%M:%S') w.timezone = timezone + dologging('debuglog.log','Start Date Time set to {s}'.format(s=w.startdatetime)) + dologging('debuglog.log','Workout Date set to {s}'.format(s=w.workoutdate)) + dologging('debuglog.log','Time zone set to {zone}'.format(zone=w.timezone)) w.save() From aa026f075ebf75cf190aa0864fd29d3ea60e792d Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 10 Jun 2021 09:22:39 +0200 Subject: [PATCH 2/3] removing print statements --- rowers/views/importviews.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 85fdd29f..53b7ab6d 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -111,7 +111,6 @@ def workout_c2_upload_view(request,id=0): try: message,c2id = c2stuff.workout_c2_upload(request.user,w,asynchron=True) except NoTokenError: # pragma: no cover - print('jet') return HttpResponseRedirect("/rowers/me/c2authorize/") messages.info(request,'Your workout will be synchronized to the Concept2 Logbook in the background') @@ -542,7 +541,6 @@ def workout_nkimport_view(request,userid=0,after=0,before=0): startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='last30') r = getrequestrower(request,userid=userid) if r.user != request.user: - print(r,r.user,request.user) messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_nkimport_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) @@ -919,7 +917,6 @@ def workout_rp3import_view(request,userid=0): def workout_stravaimport_view(request,message="",userid=0): r = getrequestrower(request,userid=userid) if r.user != request.user: - print(r,r.user,request.user) messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_stravaimport_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) @@ -1336,7 +1333,6 @@ def workout_polarimport_view(request,userid=0): # pragma: no cover def workout_sporttracksimport_view(request,message="",userid=0): r = getrequestrower(request,userid=userid) if r.user != request.user: - print(r,r.user,request.user) messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_sporttracksimport_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) @@ -1494,7 +1490,6 @@ def workout_c2import_view(request,page=1,userid=0,message=""): r = getrequestrower(request,userid=userid) if r.user != request.user: - print(r,r.user,request.user) messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_c2import_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) @@ -1611,7 +1606,6 @@ importsources = { def workout_getrp3importview(request,externalid): r = getrequestrower(request) if r.user != request.user: - print(r,r.user,request.user) messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_rp3import_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) From 0848d15a702500c4b4d00840afbfa2e21bea764e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 10 Jun 2021 09:39:46 +0200 Subject: [PATCH 3/3] adding some coverage statements --- rowers/tasks.py | 6 +++--- rowers/views/importviews.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 9e13b4a3..4c3462ec 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -3064,16 +3064,16 @@ def df_from_summary(data): try: time += split['time']/10. times.append(time) - except (KeyError, TypeError): + except (KeyError, TypeError): # pragma: no cover times.append(0) try: elapsed_distance += split['distance'] distances.append(elapsed_distance) - except (KeyError, TypeError): + except (KeyError, TypeError): # pragma: no cover distances.append(0) try: spms.append(split['stroke_rate']) - except (KeyError, TypeError): + except (KeyError, TypeError): # pragma: no cover spms.append(0) try: hrs.append(split['heart_rate']['average']) diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index 53b7ab6d..c62f4b54 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -540,7 +540,7 @@ def workout_getnkworkout_all(request,startdatestring='',enddatestring=''): def workout_nkimport_view(request,userid=0,after=0,before=0): startdate,enddate = get_dates_timeperiod(request,defaulttimeperiod='last30') r = getrequestrower(request,userid=userid) - if r.user != request.user: + if r.user != request.user: # pragma: no cover messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_nkimport_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url) @@ -1605,7 +1605,7 @@ importsources = { @permission_required('rower.is_not_freecoach',fn=get_user_by_userid, raise_exception=True) def workout_getrp3importview(request,externalid): r = getrequestrower(request) - if r.user != request.user: + if r.user != request.user: # pragma: no cover messages.error(request,'You can only access your own workouts on the NK Logbook, not those of your athletes') url = reverse('workout_rp3import_view',kwargs={'userid':request.user.id}) return HttpResponseRedirect(url)