1717from ietf .utils .test_data import make_test_data , make_review_data
1818from ietf .utils .mail import outbox , empty_outbox
1919from ietf .group .models import Group , Role , RoleName
20+ from ietf .group .factories import GroupFactory
2021from ietf .ietfauth .htpasswd import update_htpasswd_file
2122from ietf .mailinglists .models import Subscribed
22- from ietf .person .models import Person , Email
23+ from ietf .person .models import Person , Email , PersonalApiKey , PERSON_API_KEY_ENDPOINTS
2324from ietf .person .factories import PersonFactory
2425from ietf .review .models import ReviewWish , UnavailablePeriod
2526from ietf .utils .decorators import skip_coverage
@@ -497,7 +498,7 @@ def test_change_username(self):
497498 self .assertEqual (prev , user )
498499 self .assertTrue (user .check_password (u'password' ))
499500
500- def test_apikey (self ):
501+ def test_apikey_management (self ):
501502 person = PersonFactory ()
502503
503504 url = urlreverse ('ietf.ietfauth.views.apikey_index' )
@@ -511,33 +512,33 @@ def test_apikey(self):
511512 self .assertContains (r , 'Get a new personal API key' )
512513
513514 # Check the add key form content
514- url = urlreverse ('ietf.ietfauth.views.apikey_add ' )
515+ url = urlreverse ('ietf.ietfauth.views.apikey_create ' )
515516 r = self .client .get (url )
516517 self .assertContains (r , 'Create a new personal API key' )
517518 self .assertContains (r , 'Endpoint' )
518519
519520 # Add 2 keys
520- r = self .client .post (url , {'endpoint' : '/api/submit' })
521- self .assertRedirects (r , urlreverse ('ietf.ietfauth.views.apikey_index' ))
522- r = self .client .post (url , {'endpoint' : '/api/iesg/discuss' })
523- self .assertRedirects (r , urlreverse ('ietf.ietfauth.views.apikey_index' ))
521+ for endpoint , display in PERSON_API_KEY_ENDPOINTS :
522+ r = self .client .post (url , {'endpoint' : endpoint })
523+ self .assertRedirects (r , urlreverse ('ietf.ietfauth.views.apikey_index' ))
524524
525525 # Check api key list content
526526 url = urlreverse ('ietf.ietfauth.views.apikey_index' )
527527 r = self .client .get (url )
528- self . assertContains ( r , '/api/submit' )
529- self .assertContains (r , '/api/iesg/discuss' )
528+ for endpoint , display in PERSON_API_KEY_ENDPOINTS :
529+ self .assertContains (r , endpoint )
530530 q = PyQuery (r .content )
531- self .assertEqual (len (q ('td code' )), 2 )
531+ self .assertEqual (len (q ('td code' )), len (PERSON_API_KEY_ENDPOINTS )) # hash
532+ self .assertEqual (len (q ('td a:contains("Disable")' )), len (PERSON_API_KEY_ENDPOINTS ))
532533
533534 # Get one of the keys
534535 key = person .apikeys .first ()
535536
536- # Check the delete key form content
537- url = urlreverse ('ietf.ietfauth.views.apikey_del ' )
537+ # Check the disable key form content
538+ url = urlreverse ('ietf.ietfauth.views.apikey_disable ' )
538539 r = self .client .get (url )
539540
540- self .assertContains (r , 'Delete a personal API key' )
541+ self .assertContains (r , 'Disable a personal API key' )
541542 self .assertContains (r , 'Key' )
542543
543544 # Delete a key
@@ -547,7 +548,98 @@ def test_apikey(self):
547548 # Check the api key list content again
548549 url = urlreverse ('ietf.ietfauth.views.apikey_index' )
549550 r = self .client .get (url )
550- self .assertNotContains (r , key .endpoint )
551551 q = PyQuery (r .content )
552- self .assertEqual (len (q ('td code' )), 1 )
552+ self .assertEqual (len (q ('td code' )), len (PERSON_API_KEY_ENDPOINTS )) # key hash
553+ self .assertEqual (len (q ('td a:contains("Disable")' )), len (PERSON_API_KEY_ENDPOINTS )- 1 )
554+
555+ def test_apikey_usage (self ):
556+ BAD_KEY = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
557+
558+ person = PersonFactory ()
559+ area = GroupFactory (type_id = 'area' )
560+ area .role_set .create (name_id = 'ad' , person = person , email = person .email ())
561+
562+ url = urlreverse ('ietf.ietfauth.views.apikey_create' )
563+ # Check that the url is protected, then log in
564+ login_testing_unauthorized (self , person .user .username , url )
565+
566+ # Add keys
567+ for endpoint , display in PERSON_API_KEY_ENDPOINTS :
568+ r = self .client .post (url , {'endpoint' : endpoint })
569+ self .assertRedirects (r , urlreverse ('ietf.ietfauth.views.apikey_index' ))
570+
571+ for key in person .apikeys .all ()[:3 ]:
572+ url = key .endpoint
573+
574+ # successful access
575+ r = self .client .post (url , {'apikey' :key .hash (), 'dummy' :'dummy' ,})
576+ self .assertEqual (r .status_code , 200 )
577+
578+ # bad method
579+ r = self .client .put (url , {'apikey' :key .hash ()})
580+ self .assertEqual (r .status_code , 405 )
581+
582+ # missing apikey
583+ r = self .client .post (url , {'dummy' :'dummy' ,})
584+ self .assertEqual (r .status_code , 400 )
585+ self .assertIn ('Missing apikey parameter' , unicontent (r ))
586+
587+ # invalid apikey
588+ r = self .client .post (url , {'apikey' :BAD_KEY , 'dummy' :'dummy' ,})
589+ self .assertEqual (r .status_code , 400 )
590+ self .assertIn ('Invalid apikey' , unicontent (r ))
591+
592+ # too long since regular login
593+ person .user .last_login = datetime .datetime .now () - datetime .timedelta (days = settings .UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS + 1 )
594+ person .user .save ()
595+ r = self .client .post (url , {'apikey' :key .hash (), 'dummy' :'dummy' ,})
596+ self .assertEqual (r .status_code , 400 )
597+ self .assertIn ('Too long since last regular login' , unicontent (r ))
598+ person .user .last_login = datetime .datetime .now ()
599+ person .user .save ()
600+
601+ # endpoint mismatch
602+ key2 = PersonalApiKey .objects .create (person = person , endpoint = '/' )
603+ r = self .client .post (url , {'apikey' :key2 .hash (), 'dummy' :'dummy' ,})
604+ self .assertEqual (r .status_code , 400 )
605+ self .assertIn ('Apikey endpoint mismatch' , unicontent (r ))
606+ key2 .delete ()
607+
608+ def test_send_apikey_report (self ):
609+ from ietf .ietfauth .management .commands .send_apikey_usage_emails import Command
610+ from ietf .utils .mail import outbox , empty_outbox
611+
612+ person = PersonFactory ()
613+
614+ url = urlreverse ('ietf.ietfauth.views.apikey_create' )
615+ # Check that the url is protected, then log in
616+ login_testing_unauthorized (self , person .user .username , url )
617+
618+ # Add keys
619+ for endpoint , display in PERSON_API_KEY_ENDPOINTS :
620+ r = self .client .post (url , {'endpoint' : endpoint })
621+ self .assertRedirects (r , urlreverse ('ietf.ietfauth.views.apikey_index' ))
622+
623+ # Use the endpoints (the form content will not be acceptable, but the
624+ # apikey usage will be registered)
625+ count = 2
626+ # avoid usage across dates
627+ if datetime .datetime .now ().time () > datetime .time (hour = 23 , minute = 59 , second = 58 ):
628+ time .sleep (2 )
629+ for i in range (count ):
630+ for key in person .apikeys .all ():
631+ url = key .endpoint
632+ self .client .post (url , {'apikey' :key .hash (), 'dummy' : 'dummy' , })
633+ date = str (datetime .date .today ())
634+
635+ empty_outbox ()
636+ cmd = Command ()
637+ cmd .handle (verbosity = 0 , days = 7 )
638+
639+ self .assertEqual (len (outbox ), len (PERSON_API_KEY_ENDPOINTS ))
640+ for mail in outbox :
641+ body = mail .get_payload ()
642+ self .assertIn ("API key usage" , mail ['subject' ])
643+ self .assertIn (" %s times" % count , body )
644+ self .assertIn (date , body )
553645
0 commit comments