2020use OCP \AppFramework \Http ;
2121use OCP \AppFramework \Http \JSONResponse ;
2222use OCP \AppFramework \Utility \ITimeFactory ;
23+ use OCP \IDBConnection ;
2324use OCP \IRequest ;
2425use OCP \Security \Bruteforce \IThrottler ;
2526use OCP \Security \ICrypto ;
@@ -53,6 +54,8 @@ class OauthApiControllerTest extends TestCase {
5354 private $ logger ;
5455 /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
5556 private $ timeFactory ;
57+ /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
58+ private $ db ;
5659 /** @var OauthApiController */
5760 private $ oauthApiController ;
5861
@@ -69,6 +72,7 @@ protected function setUp(): void {
6972 $ this ->throttler = $ this ->createMock (IThrottler::class);
7073 $ this ->logger = $ this ->createMock (LoggerInterface::class);
7174 $ this ->timeFactory = $ this ->createMock (ITimeFactory::class);
75+ $ this ->db = $ this ->createMock (IDBConnection::class);
7276
7377 $ this ->oauthApiController = new OauthApiController (
7478 'oauth2 ' ,
@@ -81,7 +85,8 @@ protected function setUp(): void {
8185 $ this ->time ,
8286 $ this ->logger ,
8387 $ this ->throttler ,
84- $ this ->timeFactory
88+ $ this ->timeFactory ,
89+ $ this ->db ,
8590 );
8691 }
8792
@@ -316,6 +321,7 @@ public function testRefreshTokenInvalidAppToken() {
316321
317322 public function testRefreshTokenValidAppToken () {
318323 $ accessToken = new AccessToken ();
324+ $ accessToken ->setId (21 );
319325 $ accessToken ->setClientId (42 );
320326 $ accessToken ->setTokenId (1337 );
321327 $ accessToken ->setEncryptedToken ('encryptedToken ' );
@@ -367,6 +373,18 @@ public function testRefreshTokenValidAppToken() {
367373 $ this ->time ->method ('getTime ' )
368374 ->willReturn (1000 );
369375
376+ $ this ->db ->expects ($ this ->once ())
377+ ->method ('beginTransaction ' );
378+
379+ $ this ->db ->expects ($ this ->once ())
380+ ->method ('commit ' );
381+
382+ $ this ->db ->expects ($ this ->never ())
383+ ->method ('rollBack ' );
384+
385+ $ this ->tokenProvider ->expects ($ this ->never ())
386+ ->method ('invalidateToken ' );
387+
370388 $ this ->tokenProvider ->expects ($ this ->once ())
371389 ->method ('updateToken ' )
372390 ->with (
@@ -380,7 +398,7 @@ public function testRefreshTokenValidAppToken() {
380398 ->willReturn ('newEncryptedToken ' );
381399
382400 $ this ->accessTokenMapper ->expects ($ this ->once ())
383- ->method ('update ' )
401+ ->method ('rotateToken ' )
384402 ->with (
385403 $ this ->callback (function (AccessToken $ token ) {
386404 return $ token ->getHashedCode () === hash ('sha512 ' , 'random128 ' ) &&
@@ -412,6 +430,7 @@ public function testRefreshTokenValidAppToken() {
412430
413431 public function testRefreshTokenValidAppTokenBasicAuth () {
414432 $ accessToken = new AccessToken ();
433+ $ accessToken ->setId (21 );
415434 $ accessToken ->setClientId (42 );
416435 $ accessToken ->setTokenId (1337 );
417436 $ accessToken ->setEncryptedToken ('encryptedToken ' );
@@ -463,6 +482,18 @@ public function testRefreshTokenValidAppTokenBasicAuth() {
463482 $ this ->time ->method ('getTime ' )
464483 ->willReturn (1000 );
465484
485+ $ this ->db ->expects ($ this ->once ())
486+ ->method ('beginTransaction ' );
487+
488+ $ this ->db ->expects ($ this ->once ())
489+ ->method ('commit ' );
490+
491+ $ this ->db ->expects ($ this ->never ())
492+ ->method ('rollBack ' );
493+
494+ $ this ->tokenProvider ->expects ($ this ->never ())
495+ ->method ('invalidateToken ' );
496+
466497 $ this ->tokenProvider ->expects ($ this ->once ())
467498 ->method ('updateToken ' )
468499 ->with (
@@ -476,7 +507,7 @@ public function testRefreshTokenValidAppTokenBasicAuth() {
476507 ->willReturn ('newEncryptedToken ' );
477508
478509 $ this ->accessTokenMapper ->expects ($ this ->once ())
479- ->method ('update ' )
510+ ->method ('rotateToken ' )
480511 ->with (
481512 $ this ->callback (function (AccessToken $ token ) {
482513 return $ token ->getHashedCode () === hash ('sha512 ' , 'random128 ' ) &&
@@ -511,6 +542,7 @@ public function testRefreshTokenValidAppTokenBasicAuth() {
511542
512543 public function testRefreshTokenExpiredAppToken () {
513544 $ accessToken = new AccessToken ();
545+ $ accessToken ->setId (21 );
514546 $ accessToken ->setClientId (42 );
515547 $ accessToken ->setTokenId (1337 );
516548 $ accessToken ->setEncryptedToken ('encryptedToken ' );
@@ -562,6 +594,18 @@ public function testRefreshTokenExpiredAppToken() {
562594 $ this ->time ->method ('getTime ' )
563595 ->willReturn (1000 );
564596
597+ $ this ->db ->expects ($ this ->once ())
598+ ->method ('beginTransaction ' );
599+
600+ $ this ->db ->expects ($ this ->once ())
601+ ->method ('commit ' );
602+
603+ $ this ->db ->expects ($ this ->never ())
604+ ->method ('rollBack ' );
605+
606+ $ this ->tokenProvider ->expects ($ this ->never ())
607+ ->method ('invalidateToken ' );
608+
565609 $ this ->tokenProvider ->expects ($ this ->once ())
566610 ->method ('updateToken ' )
567611 ->with (
@@ -575,7 +619,7 @@ public function testRefreshTokenExpiredAppToken() {
575619 ->willReturn ('newEncryptedToken ' );
576620
577621 $ this ->accessTokenMapper ->expects ($ this ->once ())
578- ->method ('update ' )
622+ ->method ('rotateToken ' )
579623 ->with (
580624 $ this ->callback (function (AccessToken $ token ) {
581625 return $ token ->getHashedCode () === hash ('sha512 ' , 'random128 ' ) &&
@@ -604,4 +648,100 @@ public function testRefreshTokenExpiredAppToken() {
604648
605649 $ this ->assertEquals ($ expected , $ this ->oauthApiController ->getToken ('refresh_token ' , null , 'validrefresh ' , 'clientId ' , 'clientSecret ' ));
606650 }
651+
652+ public function testRefreshTokenRedeemedConcurrently (): void {
653+ $ expected = new JSONResponse ([
654+ 'error ' => 'invalid_request ' ,
655+ ], Http::STATUS_BAD_REQUEST );
656+ $ expected ->throttle (['invalid_request ' => 'token already redeemed ' ]);
657+
658+ $ accessToken = new AccessToken ();
659+ $ accessToken ->setId (21 );
660+ $ accessToken ->setClientId (42 );
661+ $ accessToken ->setTokenId (1337 );
662+ $ accessToken ->setEncryptedToken ('encryptedToken ' );
663+
664+ $ this ->accessTokenMapper ->method ('getByCode ' )
665+ ->with ('validrefresh ' )
666+ ->willReturn ($ accessToken );
667+
668+ $ client = new Client ();
669+ $ client ->setClientIdentifier ('clientId ' );
670+ $ client ->setSecret (bin2hex ('hashedClientSecret ' ));
671+ $ this ->clientMapper ->method ('getByUid ' )
672+ ->with (42 )
673+ ->willReturn ($ client );
674+
675+ $ this ->crypto
676+ ->method ('decrypt ' )
677+ ->with ('encryptedToken ' )
678+ ->willReturn ('decryptedToken ' );
679+
680+ $ this ->crypto
681+ ->method ('calculateHMAC ' )
682+ ->with ('clientSecret ' )
683+ ->willReturn ('hashedClientSecret ' );
684+
685+ $ appToken = new PublicKeyToken ();
686+ $ appToken ->setUid ('userId ' );
687+ $ this ->tokenProvider ->method ('getTokenById ' )
688+ ->with (1337 )
689+ ->willReturn ($ appToken );
690+
691+ $ this ->secureRandom ->method ('generate ' )
692+ ->willReturnCallback (function ($ len ) {
693+ return 'random ' . $ len ;
694+ });
695+
696+ $ this ->tokenProvider ->expects ($ this ->once ())
697+ ->method ('rotate ' )
698+ ->with (
699+ $ appToken ,
700+ 'decryptedToken ' ,
701+ 'random72 '
702+ )->willReturn ($ appToken );
703+
704+ $ this ->time ->method ('getTime ' )
705+ ->willReturn (1000 );
706+
707+ $ this ->tokenProvider ->expects ($ this ->once ())
708+ ->method ('updateToken ' )
709+ ->with ($ this ->isInstanceOf (PublicKeyToken::class));
710+
711+ $ this ->crypto ->method ('encrypt ' )
712+ ->with ('random72 ' , 'random128 ' )
713+ ->willReturn ('newEncryptedToken ' );
714+
715+ $ this ->db ->expects ($ this ->once ())
716+ ->method ('beginTransaction ' );
717+
718+ $ this ->db ->expects ($ this ->never ())
719+ ->method ('commit ' );
720+
721+ $ this ->db ->expects ($ this ->exactly (2 ))
722+ ->method ('inTransaction ' )
723+ ->willReturnOnConsecutiveCalls (true , false );
724+
725+ $ this ->db ->expects ($ this ->once ())
726+ ->method ('rollBack ' );
727+
728+ $ this ->tokenProvider ->expects ($ this ->once ())
729+ ->method ('invalidateToken ' )
730+ ->with ('random72 ' );
731+
732+ $ this ->accessTokenMapper ->expects ($ this ->once ())
733+ ->method ('rotateToken ' )
734+ ->with (
735+ 21 ,
736+ 'validrefresh ' ,
737+ 'random128 ' ,
738+ 'newEncryptedToken ' ,
739+ false ,
740+ )->willReturn (0 );
741+
742+ $ this ->throttler ->expects ($ this ->never ())
743+ ->method ('resetDelay ' );
744+
745+ $ this ->assertEquals ($ expected , $ this ->oauthApiController ->getToken ('refresh_token ' , null , 'validrefresh ' , 'clientId ' , 'clientSecret ' ));
746+ }
607747}
0 commit comments