@@ -25,12 +25,15 @@ import (
25
25
"github.com/coder/coder/v2/agent/agenttest"
26
26
"github.com/coder/coder/v2/coderd"
27
27
"github.com/coder/coder/v2/coderd/coderdtest"
28
+ "github.com/coder/coder/v2/coderd/coderdtest/oidctest"
28
29
"github.com/coder/coder/v2/coderd/database"
29
30
"github.com/coder/coder/v2/coderd/database/dbauthz"
30
31
"github.com/coder/coder/v2/coderd/database/dbfake"
32
+ "github.com/coder/coder/v2/coderd/database/dbgen"
31
33
"github.com/coder/coder/v2/coderd/database/dbmem"
32
34
"github.com/coder/coder/v2/coderd/database/dbtime"
33
35
"github.com/coder/coder/v2/coderd/database/pubsub"
36
+ "github.com/coder/coder/v2/coderd/externalauth"
34
37
"github.com/coder/coder/v2/coderd/rbac"
35
38
"github.com/coder/coder/v2/codersdk"
36
39
"github.com/coder/coder/v2/codersdk/agentsdk"
@@ -1536,3 +1539,94 @@ func TestWorkspaceAgent_UpdatedDERP(t *testing.T) {
1536
1539
require .True (t , ok )
1537
1540
require .Equal (t , []int {2 }, conn2 .DERPMap ().RegionIDs ())
1538
1541
}
1542
+
1543
+ func TestWorkspaceAgentExternalAuthListen (t * testing.T ) {
1544
+ t .Parallel ()
1545
+
1546
+ // ValidateURLSpam acts as a workspace calling GIT_ASK_PASS which
1547
+ // will wait until the external auth token is valid. The issue is we spam
1548
+ // the validate endpoint with requests until the token is valid. We do this
1549
+ // even if the token has not changed. We are calling validate with the
1550
+ // same inputs expecting a different result (insanity?). To reduce our
1551
+ // api rate limit usage, we should do nothing if the inputs have not
1552
+ // changed.
1553
+ //
1554
+ // Note that an expired oauth token is already skipped, so this really
1555
+ // only covers the case of a revoked token.
1556
+ t .Run ("ValidateURLSpam" , func (t * testing.T ) {
1557
+ t .Parallel ()
1558
+
1559
+ const providerID = "fake-idp"
1560
+
1561
+ // Count all the times we call validate
1562
+ validateCalls := 0
1563
+ fake := oidctest .NewFakeIDP (t , oidctest .WithServing (), oidctest .WithMiddlewares (func (handler http.Handler ) http.Handler {
1564
+ return http .Handler (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
1565
+ // Count all the validate calls
1566
+ if strings .Contains (r .URL .Path , "/external-auth-validate/" ) {
1567
+ validateCalls ++
1568
+ }
1569
+ handler .ServeHTTP (w , r )
1570
+ }))
1571
+ }))
1572
+
1573
+ ticks := make (chan time.Time )
1574
+ // setup
1575
+ ownerClient , db := coderdtest .NewWithDatabase (t , & coderdtest.Options {
1576
+ NewTicker : func (duration time.Duration ) (<- chan time.Time , func ()) {
1577
+ return ticks , func () {}
1578
+ },
1579
+ ExternalAuthConfigs : []* externalauth.Config {
1580
+ fake .ExternalAuthConfig (t , providerID , nil , func (cfg * externalauth.Config ) {
1581
+ cfg .Type = codersdk .EnhancedExternalAuthProviderGitHub .String ()
1582
+ }),
1583
+ },
1584
+ })
1585
+ first := coderdtest .CreateFirstUser (t , ownerClient )
1586
+ tmpDir := t .TempDir ()
1587
+ client , user := coderdtest .CreateAnotherUser (t , ownerClient , first .OrganizationID )
1588
+
1589
+ r := dbfake .WorkspaceBuild (t , db , database.Workspace {
1590
+ OrganizationID : first .OrganizationID ,
1591
+ OwnerID : user .ID ,
1592
+ }).WithAgent (func (agents []* proto.Agent ) []* proto.Agent {
1593
+ agents [0 ].Directory = tmpDir
1594
+ return agents
1595
+ }).Do ()
1596
+
1597
+ agentClient := agentsdk .New (client .URL )
1598
+ agentClient .SetSessionToken (r .AgentToken )
1599
+
1600
+ // We need to include an invalid oauth token that is not expired.
1601
+ dbgen .ExternalAuthLink (t , db , database.ExternalAuthLink {
1602
+ ProviderID : providerID ,
1603
+ UserID : user .ID ,
1604
+ CreatedAt : dbtime .Now (),
1605
+ UpdatedAt : dbtime .Now (),
1606
+ OAuthAccessToken : "invalid" ,
1607
+ OAuthRefreshToken : "bad" ,
1608
+ OAuthExpiry : dbtime .Now ().Add (time .Hour ),
1609
+ })
1610
+
1611
+ ctx , cancel := context .WithCancel (testutil .Context (t , testutil .WaitShort ))
1612
+ go func () {
1613
+ // The request that will block and fire off validate calls.
1614
+ _ , err := agentClient .ExternalAuth (ctx , agentsdk.ExternalAuthRequest {
1615
+ ID : providerID ,
1616
+ Match : "" ,
1617
+ Listen : true ,
1618
+ })
1619
+ assert .Error (t , err , "this should fail" )
1620
+ }()
1621
+
1622
+ // Send off 10 ticks to cause 10 validate calls
1623
+ for i := 0 ; i < 10 ; i ++ {
1624
+ ticks <- time .Now ()
1625
+ }
1626
+ cancel ()
1627
+ // We expect only 1
1628
+ // In a failed test, you will likely see 9, as the last one
1629
+ // gets cancelled.
1630
+ require .Equal (t , 1 , validateCalls , "validate calls duplicated on same token" )
1631
+ })
1632
+ }
0 commit comments